Compare commits

..

40 Commits

Author SHA1 Message Date
fujie
f061d82409 feat(async-context-compression): release v1.4.0 with structure-aware grouping and session locking
- Introduced Atomic Message Grouping to prevent tool-calling corruption (Issue #56)
- Implemented Tail Boundary Alignment for deterministic context truncation
- Added per-chat asynchronous session locking to prevent duplicate background tasks
- Enhanced summarization traceability with message IDs and names
- Synchronized version and changelog across all documentation files
- Optimized release-prep skill to remove redundant H1 titles

Closes #56
2026-03-09 20:34:34 +08:00
fujie
2eee7c5d35 fix(markdown_normalizer): adopt safe-by-default strategy for escaping
- Set 'enable_escape_fix' to False by default to prevent accidental corruption
- Improve LaTeX display math identification using regex protection
- Update documentation to reflect opt-in recommendation for escape fixes
- Fix Issue #57 remaining aggressive escaping bugs
2026-03-09 01:05:13 +08:00
fujie
9bf31488ae fix(release): correct indentation in Python script for plugin metadata extraction 2026-03-08 20:03:16 +08:00
fujie
ef86a2c3c4 fix(ci): fix EOF here-doc indentation 2026-03-08 19:52:39 +08:00
fujie
b4c6d23dfb fix(ci): fix here-doc syntax error in release workflow 2026-03-08 19:49:50 +08:00
fujie
6102851e55 fix(markdown_normalizer): enhance reliability and code protection
- Fix error fallback mechanism to guarantee 100% rollback to original text on failure
- Improve escape character cleanup to protect inline code blocks from unwanted modification
- Fix 'enable_escape_fix_in_code_blocks' configuration to correctly apply to code blocks when enabled
- Change 'show_debug_log' default to False to reduce console noise and improve privacy
- Update READMEs and docs, bumped version to 1.2.8
2026-03-08 19:48:17 +08:00
fujie
79c1fde217 fix(release): enforce single plugin update per release and improve version tagging 2026-03-08 19:42:13 +08:00
fujie
d29c24ba4a feat(openwebui-skills-manager): enhance auto-discovery and structural refactoring
- Enable default overwrite installation policy for overlapping skills
- Support deep recursive GitHub trees discovery mechanism to resolve #58
- Refactor internal architecture to fully decouple stateless helper logic
- READMEs and docs synced (v0.3.0)
2026-03-08 18:21:21 +08:00
fujie
55a9c6ffb5 docs: include Copilot SDK + Excel Expert demonstration image file 2026-03-07 23:10:30 +08:00
fujie
f11affd3e6 docs: add Copilot SDK + Excel Expert demonstration image to homeboards 2026-03-07 23:06:33 +08:00
fujie
d57f9affd5 docs(skills): add 'publish-no-version-bump' skill for code-only marketplace updates 2026-03-07 22:16:42 +08:00
fujie
f4f7b65792 fix(copilot-sdk): implement dict-based isolated cache and optimize session config
- Fix model list flapping bug by utilizing dictionary-based '_discovery_cache' keyed by config hash instead of wiping a global list.

- Optimize performance by removing redundant disk IO 'config.json' syncing ('_sync_mcp_config' and '_sync_copilot_config'); SDK directly accepts params via 'SessionConfig'.

- Remove unused imports and variables based on flake8 lint rules.
2026-03-07 22:13:58 +08:00
fujie
a777112417 fix(ci): improve release naming and baseline [skip release]
- Derive release names from changed plugin titles instead of using only the version
- Compare releases against the previous published tag across detection and commit sections
- Keep generated release note headings aligned with plugin names in release bodies
2026-03-07 04:52:59 +08:00
fujie
530a6f9459 fix(ci): stop prepending plugin readme to release notes
- Remove the auto-injected Plugin README block from release.yml
- Keep release note files as the first visible content in GitHub releases
- Prevent future releases from surfacing an unnecessary link above the changelog
2026-03-07 04:34:20 +08:00
Fu-Jie
935fa0ccaa Update LICENSE file formatting 2026-03-07 04:31:25 +08:00
fujie
f5a983fb4a feat(github-copilot-sdk): release v0.10.0 with native prompt restoration and live todo widget
- Restore native Copilot CLI prompts for authentic Plan Mode behavior
- Add SQLite-backed session management for state persistence via system prompt
- Implement Adaptive Autonomy (Agent chooses planning vs direct execution)
- Fix OpenWebUI custom tool context injection for v0.8.x compatibility
- Add compact Live TODO widget synchronized with session.db
- Upgrade SDK to github-copilot-sdk==0.1.30
- Remove legacy mode switch RPC calls (moved to prompt-driven orchestration)
- Fix intent status localization and widget whitespace optimization
- Sync bilingual READMEs and all documentation mirrors to v0.10.0
2026-03-07 04:30:15 +08:00
github-actions[bot]
35dec491de chore: update community stats - new plugin added (26 -> 27) 2026-03-06 20:12:54 +00:00
fujie
67de7f1cfc refactor(github-copilot-sdk): expand _get_chat_context to handle session_id and message_id 2026-03-05 18:44:34 +08:00
fujie
b954fbca1d refactor(github-copilot-sdk): synchronize tool extra_params with OpenWebUI 0.8.x standards 2026-03-05 18:44:34 +08:00
fujie
c1411e731d fix(github-copilot-sdk): inject __messages__, __metadata__ and __event_emitter__ into tools 2026-03-05 18:44:34 +08:00
github-actions[bot]
df78f0454b chore: update community stats - new plugin added (25 -> 26) 2026-03-04 17:19:26 +00:00
fujie
d5931fbc5e docs(smart-mind-map-tool): restore fancy note format in READMEs 2026-03-04 23:53:48 +08:00
fujie
af59959ade docs(smart-mind-map-tool): clean up README note format for community posting 2026-03-04 23:42:36 +08:00
fujie
56a6ddd422 feat(smart-mind-map-tool): initial release v1.0.0 with adaptive Rich UI 2026-03-04 23:18:21 +08:00
fujie
eda495e55f refactor(feature-request): simplify template and add plugin dropdown
- Convert Plugin Name from text input to dropdown with all plugins
- Add Tool type option to match bug report template
- Merge Alternatives Considered into Additional Information section
- Combine Motivation fields into single Motivation & Use Case field
- Reduce from 7 to 5 key fields for better UX
- Keep all critical information for feature evaluation
- Consistent structure with bug report template
2026-03-04 05:03:54 +08:00
fujie
3642058292 refactor(bug-report): simplify template for better UX
- Reduce from 17 to 9 key fields
- Merge Expected/Actual Behavior into single Description field
- Combine environment and browser into Operating System & Container field
- Move screenshots to Additional Information section
- Keep core fields essential for bug reproduction (plugin, version, OS, steps)
- Add collapsible debug information guide with frontend/server log instructions
- Improve user experience by reducing form fatigue
- Maintain all critical debugging information
2026-03-04 05:02:04 +08:00
fujie
5b0464dcdd feat(bug-report): add plugin dropdown selector
- Convert Plugin Name from text input to dropdown menu
- Include all current plugins (Actions, Filters, Pipelines, Pipes, Tools)
- Add 'Other / Not Listed' option with fallback text field
- Reduces typos and improves issue categorization accuracy
- Easier plugin-specific issue tracking and statistics
2026-03-04 04:58:12 +08:00
fujie
2aff7f1bf4 fix(bug-report): correct plugin types and environment info
- Change 'Tool (Copilot SDK)' to 'Tool' (Copilot SDK is a Pipe plugin)
- Rename Environment Details field
- Include OS and container/deployment info (Docker, Docker Compose, native, etc)
- Update checklist to reflect container/deployment requirements
- Keep OpenWebUI version as required field
- Ensures proper environment context for issue reproduction
2026-03-04 04:57:24 +08:00
fujie
ba0d63930e refactor(bug-report): simplify to OS info only
- Change Environment Information to Operating System field
- Keep only OS details (Windows/macOS/Linux versions)
- Simplify checklist requirement from full environment to OS info
- Maintains essential debugging information without overhead
2026-03-04 04:55:18 +08:00
fujie
a1568de67b enhance(bug-report): improve issue template with tool type and better logging guidance
- Add Tool (Copilot SDK) as plugin type option
- Add Environment Information field (OS, hardware, deployment)
  - Platform, device type, GPU/hardware, Python version, Docker, deployment method
- Add detailed Debug Logs & Troubleshooting guide with:
  - Frontend console logs (with steps to enable Plugin Debug Output)
  - Server-side logs (docker logs or local terminal)
- Separate Frontend Console Logs field with debug output instructions
- Add Server-Side Logs field for backend error tracking
- Add Screenshots field with guidance on what to capture
- Enhanced Checklist with environment and logs requirements
- Improves issue triage and faster bug resolution
2026-03-04 04:54:53 +08:00
fujie
f4a38a7906 feat: add issue templates for bug reports and feature requests
- Add bug_report.yml template with plugin-specific fields
- Add feature_request.yml template for enhancement suggestions
- Add config.yml to customize issue creation experience
- Templates help standardize issue reporting and improve triage
2026-03-04 04:51:40 +08:00
fujie
2e6c61737f fix(workflow): only trigger release on actual version changes
- Remove doc-only change trigger to prevent unnecessary releases
- Release should only be created when plugin versions actually change
- Keeps manual workflow_dispatch and tag-based releases intact
2026-03-04 04:45:32 +08:00
fujie
c1e9aca5dc feat(pipes/github-copilot-sdk): add strict file link validation rules
- Add Strict Link Validity Rule (CRITICAL) forbidding fabricated URLs
- Implement Failure Handling for non-successful publish operations
- Establish Allowed Link Formats (Whitelist) with exact patterns
- Define Invalid Link Examples (Forbidden) including /c/... and relative paths
- Add Auto-Correction Rule requiring discard of non-whitelisted links
- Enhance URL Format guidance to emphasize verbatim copying without modification
- Add Workspace Visibility Hint (#10) for workspace file inspection context
2026-03-04 04:14:48 +08:00
Fu-Jie
6f700fe610 Fix capitalization in README description
Corrected capitalization of 'OpenWebUI' to 'open-webui'.
2026-03-04 04:11:16 +08:00
fujie
3927e384cc docs(skill): add open-terminal project to source-code-analyzer 2026-03-04 02:20:24 +08:00
fujie
e1dac2219e fix(stats): stabilize Top6 version badges and fix Excel version display [skip release]
- replace rank-based p{idx}_version usage with per-post static version badge generation

- set Export to Excel version badge to v0.3.7 in home README and README_CN
2026-03-04 01:59:31 +08:00
fujie
9436364b9a docs(readme): restore version badge for Copilot SDK; ci: add skip-release support and README link injection [skip release]
- add static v0.9.1 badge to Copilot SDK Star Features heading

- inject plugin README link before each embedded v*.md in release body

- skip release workflow when commit message contains [skip release]
2026-03-04 01:34:24 +08:00
fujie
e7b1ff4c54 docs(readme): clean up Star Features badges and fix Copilot SDK stats badge keys
- remove all Market badges from Star Features headings

- replace invalid p0_dl/p0_vw placeholders with correct per-post MD5-keyed Gist badges

- remove GitHub Copilot SDK row from Top 6 table (not in ranked stats yet)
2026-03-04 01:20:35 +08:00
fujie
c4ff4fea7e ci(release): reorder release body and improve commits format
- move v*.md release notes to top of release body

- commits now show subject (bold) + body lines per Conventional Commits best practice
2026-03-04 01:19:34 +08:00
fujie
32afc3286e ci(release): fix release notes format
- remove file path header (### plugins/...) from embedded release note content

- exclude _CN.md files to avoid Chinese/English mixed output

- remove Documentation Changes file list and Additional Documentation Files section
2026-03-04 01:06:54 +08:00
159 changed files with 20220 additions and 2424 deletions

View File

@@ -0,0 +1,46 @@
# `.agent/learnings/` — Engineering Learnings & Reusable Patterns
This directory stores **hard-won engineering insights** discovered during development.
Each file is a standalone Markdown note covering a specific topic, pattern, or gotcha.
The goal is to avoid re-investigating the same issue twice.
---
## Conventions
- **File naming**: `{topic}.md`, e.g., `openwebui-tool-injection.md`
- **Scope**: One clear topic per file. Keep files focused and concise.
- **Format**: Use the template below.
---
## Template
```markdown
# [Topic Title]
> Discovered: YYYY-MM-DD
## Context
Where / when does this apply?
## Finding
What exactly did we learn?
## Solution / Pattern
The code or approach that works.
## Gotchas
Edge cases or caveats to watch out for.
```
---
## Index
| File | Topic |
|------|-------|
| [openwebui-tool-injection.md](./openwebui-tool-injection.md) | How OpenWebUI injects parameters into Tool functions, and what the Pipe must provide |
| [openwebui-mock-request.md](./openwebui-mock-request.md) | How to build a valid Mock Request for calling OpenWebUI-internal APIs from a Pipe |
| [copilot-plan-mode-prompt-parity.md](./copilot-plan-mode-prompt-parity.md) | Why Plan Mode prompt logic must be shared between fresh-session and resume-session injection |

View File

@@ -0,0 +1,40 @@
# Copilot Plan Mode Prompt Parity
> Discovered: 2026-03-06
## Context
The GitHub Copilot SDK pipe builds system prompts in two paths:
- fresh session creation via `_build_session_config(...)`
- resumed session injection via the `system_parts` rebuild branch
Plan Mode guidance was duplicated across those branches.
## Finding
If Plan Mode instructions are edited in only one branch, resumed sessions silently lose planning behavior or capability hints that fresh sessions still have.
This is especially easy to miss because both branches still work, but resumed chats receive a weaker or stale prompt.
Session mode switching alone is also not enough. Even when `session.rpc.mode.set(Mode.PLAN)` succeeds, the SDK may still skip creating the expected `plan.md` if the runtime system prompt does not explicitly include the original Plan Mode persistence contract.
## Solution / Pattern
Extract the Plan Mode prompt into one shared helper and call it from both branches:
```python
def _build_plan_mode_context(plan_path: str) -> str:
...
```
Then inject it in both places with the chat-specific `plan.md` path.
For extra safety, when the pipe later reads `session.rpc.plan.read()`, mirror the returned content into the chat-specific `COPILOTSDK_CONFIG_DIR/session-state/<chat_id>/plan.md` path. This keeps the UI-visible file in sync even if the SDK persists plan state internally but does not materialize the file where the chat integration expects it.
## Gotchas
- Keep the helper dynamic: the `plan.md` path must still be resolved per chat/session.
- Do not only update debug prompt artifacts; the effective runtime prompt lives in `plugins/pipes/github-copilot-sdk/github_copilot_sdk.py`.
- Resume-session parity matters for capability guidance just as much as for session context.
- If users report that Plan Mode is active but `plan.md` is missing, check both halves: prompt parity and the final `rpc.plan.read()` -> `plan.md` sync path.

View File

@@ -0,0 +1,131 @@
# Building a Valid Mock Request for OpenWebUI Pipes
> Discovered: 2026-03-05
## Context
OpenWebUI Pipes run as a Pipe plugin, not as a real HTTP request handler. When the Pipe
needs to call OpenWebUI-internal APIs (like `generate_chat_completion`, `get_tools`, etc.)
or load Tools that do the same, it must provide a **fake-but-complete Request object**.
## Finding
OpenWebUI's internal functions expect `request` to satisfy several contracts:
```
request.app.state.MODELS → dict { model_id: ModelModel } — MUST be populated!
request.app.state.config → config object with all env variables
request.app.state.TOOLS → dict (can start empty)
request.app.state.FUNCTIONS → dict (can start empty)
request.app.state.redis → None is fine
request.app.state.TOOL_SERVERS → [] is fine
request.app.url_path_for(name, **path_params) → str
request.headers → dict with Authorization, host, user-agent
request.state.user → user dict
request.state.token.credentials → str (the Bearer token, without "Bearer " prefix)
await request.json() → dict (the raw request body)
await request.body() → bytes (the raw request body as JSON bytes)
```
## Solution / Pattern
```python
from types import SimpleNamespace
import json as _json_mod
def _build_openwebui_request(user: dict, token: str, body: dict = None):
from open_webui.config import PERSISTENT_CONFIG_REGISTRY
from open_webui.models.models import Models as _Models
# 1. Build config from registry
config = SimpleNamespace()
for item in PERSISTENT_CONFIG_REGISTRY:
val = item.value
if hasattr(val, "value"):
val = val.value
setattr(config, item.env_name, val)
# 2. Populate MODELS from DB — critical for model validation
system_models = {}
try:
for m in _Models.get_all_models():
system_models[m.id] = m
except Exception:
pass
# 3. Build app_state
app_state = SimpleNamespace(
config=config,
TOOLS={},
TOOL_CONTENTS={},
FUNCTIONS={},
FUNCTION_CONTENTS={},
MODELS=system_models, # <-- KEY: must not be empty!
redis=None,
TOOL_SERVERS=[],
)
# 4. url_path_for helper
def url_path_for(name: str, **params):
if name == "get_file_content_by_id":
return f"/api/v1/files/{params.get('id')}/content"
return f"/mock/{name}"
app = SimpleNamespace(state=app_state, url_path_for=url_path_for)
# 5. Async body helpers
async def _json():
return body or {}
async def _body_fn():
return _json_mod.dumps(body or {}).encode("utf-8")
# 6. Headers
headers = {
"user-agent": "Mozilla/5.0",
"host": "localhost:8080",
"accept": "*/*",
}
if token:
headers["Authorization"] = token if token.startswith("Bearer ") else f"Bearer {token}"
return SimpleNamespace(
app=app,
headers=headers,
method="POST",
cookies={},
base_url="http://localhost:8080",
url=SimpleNamespace(path="/api/chat/completions", base_url="http://localhost:8080"),
state=SimpleNamespace(
token=SimpleNamespace(credentials=token or ""),
user=user or {},
),
json=_json,
body=_body_fn,
)
```
## Token Extraction
Tokens can be found in multiple places. Check in order:
```python
# 1. Direct in body (some SDK requests embed it)
token = body.get("token")
# 2. In metadata
token = token or (metadata or {}).get("token")
# 3. In the original __request__ Authorization header
if not token and __request__ is not None:
auth = getattr(__request__, "headers", {}).get("Authorization", "")
if auth.startswith("Bearer "):
token = auth.split(" ", 1)[1]
```
## Gotchas
- **`app.state.MODELS` empty = "Model not found"** for *any* model ID, even correct ones.
- `TOOL_SERVER_CONNECTIONS` must be synced from DB, not from in-memory cache (stale in multi-worker).
- `request.state.token.credentials` should be the **raw token** (no "Bearer " prefix).
- Tools may call `await request.json()` — must be an async method, not a regular attribute.

View File

@@ -0,0 +1,83 @@
# OpenWebUI Tool Parameter Injection
> Discovered: 2026-03-05
## Context
When OpenWebUI loads a Python Tool and calls one of its functions (e.g. `generate_mind_map`),
it automatically matches parameters from an `extra_params` dict against the function's
signature **by name**. This is done in:
```
open_webui/utils/tools.py → get_async_tool_function_and_apply_extra_params()
```
The lookup is: `extra_params = {k: v for k, v in extra_params.items() if k in sig.parameters}`
## Finding
A Tool function declares its dependencies via its parameter names. Common injected names:
| Parameter Name | What it contains |
|-----------------------|---------------------------------------------------|
| `__user__` | User context dict (id, email, role, name) |
| `__event_emitter__` | Async callable to emit status/notification events |
| `__event_call__` | Async callable for JS `__event_call__` roundtrips |
| `__request__` | Request-like object (must have `.app.state.MODELS`) |
| `__metadata__` | Dict: `{model, base_model_id, chat_id, ...}` |
| `__messages__` | Full conversation history list |
| `__chat_id__` | Current chat UUID |
| `__message_id__` | Current message UUID |
| `__session_id__` | Current session UUID |
| `__files__` | Files attached to the current message |
| `__task__` | Task type string (e.g. `title_generation`) |
| `body` | Raw request body dict (non-dunder variant) |
| `request` | Request object (non-dunder variant) |
## Key Rule
**`extra_params` must contain ALL keys a Tool's function signature declares.**
If a key is missing from `extra_params`, the parameter silently receives its default
value (e.g. `{}` for `__metadata__`). This means the Tool appears to work but
gets empty/wrong context.
## Solution / Pattern
When a Pipe calls an OpenWebUI Tool, it must populate `extra_params` with **all** the above:
```python
extra_params = {
"__request__": request, # Must have app.state.MODELS populated!
"request": request, # Non-dunder alias
"__user__": user_data,
"__event_emitter__": __event_emitter__,
"__event_call__": __event_call__,
"__messages__": messages,
"__metadata__": __metadata__ or {},
"__chat_id__": chat_id,
"__message_id__": message_id,
"__session_id__": session_id,
"__files__": files,
"__task__": task,
"__task_body__": task_body,
"body": body, # Non-dunder alias
...
}
```
## Model Resolution
Tools that call `generate_chat_completion` internally need a **valid model ID**.
When the conversation is running under a Pipe/Manifold model (e.g. `github_copilot.gpt-4o`),
the Tool's `valves.MODEL_ID` must be a *real* model known to the system.
`generate_chat_completion` validates model IDs against `request.app.state.MODELS`.
➡️ That dict **must be populated** from the database (see `openwebui-mock-request.md`).
## Gotchas
- Tools call `generate_chat_completion` with a `request` arg that must be the full Mock Request.
- If `app.state.MODELS` is empty, even a correctly-spelled model ID will cause "Model not found".
- `__metadata__['model']` can be a **dict** (from DB) **or a string** (manifold ID). Tools must
handle both types.
- For manifold models not in the DB, strip the prefix: `github_copilot.gpt-4o``gpt-4o`.

View File

@@ -138,6 +138,18 @@ Before completing an antigravity operation, confirm:
- [ ] 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
- [ ] Non-obvious findings / gotchas are saved to `.agent/learnings/{topic}.md`
---
## Mandatory: Knowledge Capture
Any non-obvious pattern, internal API contract, or workaround discovered during an
antigravity session **MUST** be saved to `.agent/learnings/{topic}.md` before the
session ends. This ensures hard-won insights are not lost between sessions.
**Format**: See `.agent/learnings/README.md`
**Existing entries**: Browse `.agent/learnings/` for prior knowledge to reuse.
---
@@ -145,3 +157,4 @@ Before completing an antigravity operation, confirm:
- Full engineering spec: `.github/copilot-instructions.md` → Section: **Antigravity Development Mode**
- Design document: `docs/development/copilot-engineering-plan.md` → Section 5
- Knowledge base: `.agent/learnings/` — reusable engineering patterns and gotchas

71
.agent/skills/README.md Normal file
View File

@@ -0,0 +1,71 @@
# Agent Skills Index
This folder contains reusable Agent Skills for GitHub Copilot / VS Code custom agent workflows.
## Available Skills
- **community-announcer**
- Purpose: Generate community announcement content and related assets.
- Entry: `community-announcer/SKILL.md`
- **doc-mirror-sync**
- Purpose: Sync mirrored documentation content and helper scripts.
- Entry: `doc-mirror-sync/SKILL.md`
- **gh-issue-replier**
- Purpose: Draft standardized issue replies with templates.
- Entry: `gh-issue-replier/SKILL.md`
- **gh-issue-scheduler**
- Purpose: Schedule and discover unanswered issues for follow-up.
- Entry: `gh-issue-scheduler/SKILL.md`
- **i18n-validator**
- Purpose: Validate translation key consistency across i18n dictionaries.
- Entry: `i18n-validator/SKILL.md`
- **plugin-scaffolder**
- Purpose: Scaffold OpenWebUI plugin boilerplate with repository standards.
- Entry: `plugin-scaffolder/SKILL.md`
- **version-bumper**
- Purpose: Assist with semantic version bumping workflows.
- Entry: `version-bumper/SKILL.md`
- **xlsx-single-file**
- Purpose: Single-file spreadsheet operations workflow without LibreOffice.
- Entry: `xlsx-single-file/SKILL.md`
---
## Release Pipeline Skills
These four skills form a complete release pipeline and are designed to be used in sequence:
```
release-prep → pr-submitter → pr-reviewer → release-finalizer
(prepare) (push & PR) (respond to review) (merge & close issue)
```
- **release-prep**
- Purpose: Full release preparation — version sync across 7+ files, bilingual release notes creation, consistency check, and commit.
- Entry: `release-prep/SKILL.md`
- **pr-submitter**
- Purpose: Shell-escape-safe PR submission — writes body to temp file, validates sections, pushes branch, creates PR via `gh pr create --body-file`.
- Entry: `pr-submitter/SKILL.md`
- **pr-reviewer**
- Purpose: Fetch PR review comments, categorize feedback, implement fixes, commit and push, reply to reviewers.
- Entry: `pr-reviewer/SKILL.md`
- **release-finalizer**
- Purpose: Merge release PR to main with proper commit message, auto-link and close related issues, post closing messages.
- Entry: `release-finalizer/SKILL.md`
## Notes
- Skill definitions follow the expected location pattern:
- `.github/skills/<skill-name>/SKILL.md`
- Each skill may include optional `assets/`, `references/`, and `scripts/` folders.
- This directory mirrors `.gemini/skills` for compatibility.

View File

@@ -0,0 +1,23 @@
---
name: community-announcer
description: Drafts engaging English and Chinese update announcements for the OpenWebUI Community and other social platforms. Use when a new version is released.
---
# Community Announcer
## Overview
Automates the drafting of high-impact update announcements.
## Workflow
1. **Source Intel**: Read the latest version's `What's New` section from `README.md`.
2. **Drafting**: Create two versions:
- **Community Post**: Professional, structured, technical.
- **Catchy Short**: For Discord/Twitter, use emojis and bullet points.
3. **Multi-language**: Generate BOTH English and Chinese versions automatically.
## Announcement Structure (Recommended)
- **Headline**: "Update vX.X.X - [Main Feature]"
- **Introduction**: Brief context.
- **Key Highlights**: Bulleted list of fixes/features.
- **Action**: "Download from [Market Link]"
- **Closing**: Thanks and Star request.

View File

@@ -0,0 +1,50 @@
---
name: doc-mirror-sync
description: Automatically synchronizes plugin READMEs to the official documentation directory (docs/). Use after editing a plugin's local documentation to keep the MkDocs site up to date.
---
# Doc Mirror Sync
## Overview
Automates the mirroring of `plugins/{type}/{name}/README.md` to `docs/plugins/{type}/{name}.md`.
## Docs-Only Mode (No Release Changes)
Use this mode when the request is "only sync docs".
- Only update documentation mirror files under `docs/plugins/**`.
- Do **not** bump plugin version.
- Do **not** modify plugin code (`plugins/**.py`) unless explicitly requested.
- Do **not** update root badges/dates for release.
- Do **not** run release preparation steps.
## Workflow
1. Identify changed READMEs.
2. Copy content to corresponding mirror paths.
3. Update version badges in `docs/plugins/{type}/index.md`.
## Commands
### Sync all mirrors (EN + ZH)
```bash
python .github/skills/doc-mirror-sync/scripts/sync.py
```
### Sync only one plugin (EN only)
```bash
cp plugins/<type>/<name>/README.md docs/plugins/<type>/<name>.md
```
### Sync only one plugin (EN + ZH)
```bash
cp plugins/<type>/<name>/README.md docs/plugins/<type>/<name>.md
cp plugins/<type>/<name>/README_CN.md docs/plugins/<type>/<name>.zh.md
```
## Notes
- If asked for English-only update, sync only `README.md` -> `.md` mirror.
- If both languages are requested, sync both `README.md` and `README_CN.md`.
- After syncing, verify git diff only contains docs file changes.

View File

@@ -0,0 +1,38 @@
#!/usr/bin/env python3
import os
import shutil
import re
def sync_mirrors():
plugins_root = "plugins"
docs_root = "docs/plugins"
types = ["actions", "filters", "pipes", "pipelines", "tools"]
for t in types:
src_type_dir = os.path.join(plugins_root, t)
dest_type_dir = os.path.join(docs_root, t)
if not os.path.exists(src_type_dir): continue
os.makedirs(dest_type_dir, exist_ok=True)
for name in os.listdir(src_type_dir):
plugin_dir = os.path.join(src_type_dir, name)
if not os.path.isdir(plugin_dir): continue
# Sync README.md -> docs/plugins/{type}/{name}.md
src_readme = os.path.join(plugin_dir, "README.md")
if os.path.exists(src_readme):
dest_readme = os.path.join(dest_type_dir, f"{name}.md")
shutil.copy(src_readme, dest_readme)
print(f"✅ Mirrored: {t}/{name} (EN)")
# Sync README_CN.md -> docs/plugins/{type}/{name}.zh.md
src_readme_cn = os.path.join(plugin_dir, "README_CN.md")
if os.path.exists(src_readme_cn):
dest_readme_zh = os.path.join(dest_type_dir, f"{name}.zh.md")
shutil.copy(src_readme_cn, dest_readme_zh)
print(f"✅ Mirrored: {t}/{name} (ZH)")
if __name__ == "__main__":
sync_mirrors()

View File

@@ -0,0 +1,51 @@
---
name: gh-issue-replier
description: Professional English replier for GitHub issues. Use when a task is completed, a bug is fixed, or more info is needed from the user. Automates replying using the 'gh' CLI tool.
---
# Gh Issue Replier
## Overview
The `gh-issue-replier` skill enables Gemini CLI to interact with GitHub issues professionally. It enforces English for all communications and leverages the `gh` CLI to post comments.
## Workflow
1. **Identify the Issue**: Find the issue number (e.g., #49).
2. **Check Star Status**: Run the bundled script to check if the author has starred the repo.
* Command: `bash scripts/check_star.sh <issue-number>`
* Interpretation:
* Exit code **0**: User has starred. Use "Already Starred" templates.
* Exit code **1**: User has NOT starred. Include "Star Request" in the reply.
3. **Select a Template**: Load [templates.md](references/templates.md) to choose a suitable English response pattern.
4. **Draft the Reply**: Compose a concise message based on the star status.
5. **Post the Comment**: Use the `gh` tool to submit the reply.
## Tool Integration
### Check Star Status
```bash
bash scripts/check_star.sh <issue-number>
```
### Post Comment
```bash
gh issue comment <issue-number> --body "<message-body>"
```
Example (if user has NOT starred):
```bash
gh issue comment 49 --body "This has been fixed in v1.2.7. If you find this helpful, a star on the repo would be much appreciated! ⭐"
```
Example (if user HAS starred):
```bash
gh issue comment 49 --body "This has been fixed in v1.2.7. Thanks for your support!"
```
## Guidelines
- **Language**: ALWAYS use English for the comment body, even if the system prompt or user conversation is in another language.
- **Tone**: Professional, helpful, and appreciative.
- **Precision**: When announcing a fix, mention the specific version or the logic change (e.g., "Updated regex pattern").
- **Closing**: If the issue is resolved and you have permission, you can also use `gh issue close <number>`.

View File

@@ -0,0 +1,17 @@
# Reference Documentation for Gh Issue Replier
This is a placeholder for detailed reference documentation.
Replace with actual reference content or delete if not needed.
## Structure Suggestions
### API Reference Example
- Overview
- Authentication
- Endpoints with examples
- Error codes
### Workflow Guide Example
- Prerequisites
- Step-by-step instructions
- Best practices

View File

@@ -0,0 +1,45 @@
# Issue Reply Templates
Use these templates to craft professional English replies. Adjust placeholders like `@username`, `v1.2.x`, and `[commit hash]` as needed.
## 1. Acknowledging a New Issue
Use when you first see an issue and want to let the user know you are working on it.
- "Thank you for reporting this! I'm looking into it right now."
- "Thanks for bringing this to my attention. I'll try to reproduce this behavior and get back to you shortly."
## 2. Requesting More Information
Use when you need logs or specific details to fix the bug.
- "Could you please provide the **'Original'** vs **'Normalized'** content from your browser console logs (F12)? It would help a lot in debugging."
- "It would be very helpful if you could share the specific Markdown text that triggered this issue."
## 3. Announcing a Fix
Use when you have pushed the fix to the repository.
- "This has been fixed in version **v1.2.x**. You can update the plugin to resolve it."
- "I've just pushed a fix for this in [commit hash]. Please let me know if it works for you after updating."
- "The issue was caused by a greedy regex pattern. I've updated it to use a tempered greedy token to prevent incorrect merging."
## 4. Guiding to Official Market
Always provide the official market link to ensure the user gets the latest verified version.
- "The fix is now live! You can download the latest version from the official OpenWebUI Community page here: [Plugin Market Link]. Simply update the function in your OpenWebUI instance to apply the changes."
- "I recommend getting the updated version from the official store: [Link]. It includes the fix for the spacing issue we discussed."
## 5. Closing the Issue
Use when the issue is confirmed resolved.
- "Glad to hear it's working now! Closing this for now. Feel free to reopen it if the problem persists."
- "Since this is resolved, I'm closing this issue. Thanks again for your feedback!"
## 5. Pro-tip: Star Request
Gently handle star requests based on the user's current status.
### If User has NOT starred:
- "If you find this plugin helpful, a star on the repo would be much appreciated! ⭐"
- "We'd love your support! If this fixed your issue, please consider starring the repository. ⭐"
### If User HAS already starred:
- "Thanks again for starring the project and for your continuous support!"
- "I appreciate your support and for being a stargazer of this project!"

View File

@@ -0,0 +1,31 @@
#!/usr/bin/env bash
# Robust Star Checker v2
# Usage: ./check_star.sh <issue_number>
ISSUE_NUM=$1
if [ -z "$ISSUE_NUM" ]; then exit 2; fi
# 1. Get Repo and Author info
REPO_FULL=$(gh repo view --json owner,name -q ".owner.login + \"/\" + .name")
USER_LOGIN=$(gh issue view "$ISSUE_NUM" --json author -q ".author.login")
# 2. Use GraphQL for high precision (Detects stars even when REST 404s)
IS_STARRED=$(gh api graphql -f query='
query($owner:String!, $repo:String!, $user:String!) {
repository(owner:$owner, name:$repo) {
stargazers(query:$user, first:1) {
nodes {
login
}
}
}
}' -f owner="${REPO_FULL%/*}" -f repo="${REPO_FULL#*/}" -f user="$USER_LOGIN" -q ".data.repository.stargazers.nodes[0].login")
if [ "$IS_STARRED" == "$USER_LOGIN" ]; then
echo "Confirmed: @$USER_LOGIN HAS starred $REPO_FULL. ⭐"
exit 0
else
echo "Confirmed: @$USER_LOGIN has NOT starred $REPO_FULL."
exit 1
fi

View File

@@ -0,0 +1,42 @@
---
name: gh-issue-scheduler
description: Finds all open GitHub issues that haven't been replied to by the owner, summarizes them, and generates a solution plan. Use when the user wants to audit pending tasks or plan maintenance work.
---
# Gh Issue Scheduler
## Overview
The `gh-issue-scheduler` skill helps maintainers track community feedback by identifying unaddressed issues and drafting actionable technical plans to resolve them.
## Workflow
1. **Identify Unanswered Issues**: Run the bundled script to fetch issues without owner replies.
* Command: `bash scripts/find_unanswered.sh`
2. **Analyze and Summarize**: For each identified issue, summarize the core problem and the user's intent.
3. **Generate Solution Plans**: Draft a technical "Action Plan" for each issue, including:
* **Root Cause Analysis** (if possible)
* **Proposed Fix/Implementation**
* **Verification Strategy**
4. **Present to User**: Display a structured report of all pending issues and their respective plans.
## Tool Integration
### Find Unanswered Issues
```bash
bash scripts/find_unanswered.sh
```
## Report Format
When presenting the summary, use the following Markdown structure:
### 📋 Unanswered Issues Audit
#### Issue #[Number]: [Title]
- **Author**: @username
- **Summary**: Concise description of the problem.
- **Action Plan**:
1. Step 1 (e.g., Investigate file X)
2. Step 2 (e.g., Apply fix Y)
3. Verification (e.g., Run test Z)

View File

@@ -0,0 +1,42 @@
#!/usr/bin/env bash
# Fetch all open issues and filter for those without responses from the owner/collaborators.
# Uses 'gh' CLI.
REPO_FULL=$(gh repo view --json owner,name -q ".owner.login + "/" + .name")
OWNER=${REPO_FULL%/*}
# 1. Get all open issues
OPEN_ISSUES=$(gh issue list --state open --json number,title,author,createdAt --limit 100)
echo "Analysis for repository: $REPO_FULL"
echo "------------------------------------"
# Process each issue
echo "$OPEN_ISSUES" | jq -c '.[]' | while read -r issue; do
NUMBER=$(echo "$issue" | jq -r '.number')
TITLE=$(echo "$issue" | jq -r '.title')
AUTHOR=$(echo "$issue" | jq -r '.author.login')
# Check comments for owner responses
# We look for comments where the author is the repo owner
COMMENTS=$(gh issue view "$NUMBER" --json comments -q ".comments[].author.login" 2>/dev/null)
HAS_OWNER_REPLY=false
for COMMENT_AUTHOR in $COMMENTS; do
if [ "$COMMENT_AUTHOR" == "$OWNER" ]; then
HAS_OWNER_REPLY=true
break
fi
done
if [ "$HAS_OWNER_REPLY" == "false" ]; then
echo "ISSUE_START"
echo "ID: $NUMBER"
echo "Title: $TITLE"
echo "Author: $AUTHOR"
echo "Description:"
gh issue view "$NUMBER" --json body -q ".body"
echo "ISSUE_END"
fi
done

View File

@@ -0,0 +1,14 @@
---
name: i18n-validator
description: Validates multi-language consistency in the TRANSLATIONS dictionary of a plugin. Use to check if any language keys are missing or if translations need updating.
---
# I18n Validator
## Overview
Ensures all 12 supported languages (en-US, zh-CN, etc.) have aligned translation keys.
## Features
- Detects missing keys in non-English dictionaries.
- Suggests translations using the core AI engine.
- Validates the `fallback_map` for variant redirects.

View File

@@ -0,0 +1,54 @@
#!/usr/bin/env python3
import sys
import ast
import os
def check_i18n(file_path):
if not os.path.exists(file_path):
print(f"Error: File not found {file_path}")
return
with open(file_path, 'r', encoding='utf-8') as f:
tree = ast.parse(f.read())
translations = {}
for node in tree.body:
if isinstance(node, ast.Assign):
for target in node.targets:
if isinstance(target, ast.Name) and target.id == "TRANSLATIONS":
translations = ast.literal_eval(node.value)
break
if not translations:
print("⚠️ No TRANSLATIONS dictionary found.")
return
# Base keys from English
base_lang = "en-US"
if base_lang not in translations:
print(f"❌ Error: {base_lang} missing in TRANSLATIONS.")
return
base_keys = set(translations[base_lang].keys())
print(f"🔍 Analyzing {file_path}...")
print(f"Standard keys ({len(base_keys)}): {', '.join(sorted(base_keys))}
")
for lang, keys in translations.items():
if lang == base_lang: continue
lang_keys = set(keys.keys())
missing = base_keys - lang_keys
extra = lang_keys - base_keys
if missing:
print(f"{lang}: Missing {len(missing)} keys: {', '.join(missing)}")
if extra:
print(f"⚠️ {lang}: Has {len(extra)} extra keys: {', '.join(extra)}")
if not missing and not extra:
print(f"{lang}: Aligned.")
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: validate_i18n.py <path_to_plugin.py>")
sys.exit(1)
check_i18n(sys.argv[1])

View File

@@ -0,0 +1,19 @@
---
name: plugin-scaffolder
description: Generates a standardized single-file i18n Python plugin template based on project standards. Use when starting a new plugin development to skip boilerplate writing.
---
# Plugin Scaffolder
## Overview
Generates compliant OpenWebUI plugin templates with built-in i18n, common utility methods, and required docstring fields.
## Usage
1. Provide the **Plugin Name** and **Type** (action/filter/pipe).
2. The skill will generate the `.py` file and the bilingual `README` files.
## Template Standard
- `Valves(BaseModel)` with `UPPER_SNAKE_CASE`
- `_get_user_context` with JS fallback and timeout
- `_emit_status` and `_emit_debug_log` methods
- Standardized docstring metadata

View File

@@ -0,0 +1,34 @@
# {{TITLE}}
**Author:** [Fu-Jie](https://github.com/Fu-Jie/openwebui-extensions) | **Version:** 0.1.0 | **Project:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **License:** MIT
{{DESCRIPTION}}
## 🔥 What's New in v0.1.0
* Initial release of {{TITLE}}.
## 🌐 Multilingual Support
Supports automatic interface and status switching for the following languages:
`English`, `简体中文`, `繁體中文 (香港)`, `繁體中文 (台灣)`, `한국어`, `日本語`, `Français`, `Deutsch`, `Español`, `Italiano`, `Tiếng Việt`, `Bahasa Indonesia`.
## ✨ Core Features
* Feature 1
* Feature 2
## How to Use 🛠️
1. Install the plugin in Open WebUI.
2. Configure settings in Valves.
## Configuration (Valves) ⚙️
| Parameter | Default | Description |
| :--- | :--- | :--- |
| `priority` | `50` | Execution priority. |
## ⭐ Support
If this plugin has been useful, a star on [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) is a big motivation for me. Thank you for the support.

View File

@@ -0,0 +1,80 @@
"""
title: {{TITLE}}
author: Fu-Jie
author_url: https://github.com/Fu-Jie/openwebui-extensions
funding_url: https://github.com/open-webui
version: 0.1.0
description: {{DESCRIPTION}}
"""
import asyncio
import logging
import json
from typing import Optional, Dict, Any, List, Callable, Awaitable
from pydantic import BaseModel, Field
from fastapi import Request
# Configure logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
logger = logging.getLogger(__name__)
TRANSLATIONS = {
"en-US": {"status_starting": "Starting {{TITLE}}..."},
"zh-CN": {"status_starting": "正在启动 {{TITLE}}..."},
"zh-HK": {"status_starting": "正在啟動 {{TITLE}}..."},
"zh-TW": {"status_starting": "正在啟動 {{TITLE}}..."},
"ko-KR": {"status_starting": "{{TITLE}} 시작 중..."},
"ja-JP": {"status_starting": "{{TITLE}} を起動中..."},
"fr-FR": {"status_starting": "Démarrage de {{TITLE}}..."},
"de-DE": {"status_starting": "{{TITLE}} wird gestartet..."},
"es-ES": {"status_starting": "Iniciando {{TITLE}}..."},
"it-IT": {"status_starting": "Avvio di {{TITLE}}..."},
"vi-VN": {"status_starting": "Đang khởi động {{TITLE}}..."},
"id-ID": {"status_starting": "Memulai {{TITLE}}..."},
}
class {{CLASS_NAME}}:
class Valves(BaseModel):
priority: int = Field(default=50, description="Priority level (lower = earlier).")
show_status: bool = Field(default=True, description="Show status updates in UI.")
def __init__(self):
self.valves = self.Valves()
self.fallback_map = {
"zh": "zh-CN", "en": "en-US", "ko": "ko-KR", "ja": "ja-JP",
"fr": "fr-FR", "de": "de-DE", "es": "es-ES", "it": "it-IT",
"vi": "vi-VN", "id": "id-ID"
}
def _get_translation(self, lang: str, key: str, **kwargs) -> str:
target_lang = lang
if target_lang not in TRANSLATIONS:
base = target_lang.split("-")[0]
target_lang = self.fallback_map.get(base, "en-US")
lang_dict = TRANSLATIONS.get(target_lang, TRANSLATIONS["en-US"])
text = lang_dict.get(key, TRANSLATIONS["en-US"].get(key, key))
return text.format(**kwargs) if kwargs else text
async def _get_user_context(self, __user__: Optional[dict], __event_call__: Optional[Callable] = None, __request__: Optional[Request] = None) -> dict:
user_data = __user__ if isinstance(__user__, dict) else {}
user_language = user_data.get("language", "en-US")
if __event_call__:
try:
js = "try { return (document.documentElement.lang || localStorage.getItem('locale') || navigator.language || 'en-US'); } catch (e) { return 'en-US'; }"
frontend_lang = await asyncio.wait_for(__event_call__({"type": "execute", "data": {"code": js}}), timeout=2.0)
if frontend_lang: user_language = frontend_lang
except: pass
return {"user_language": user_language}
async def {{METHOD_NAME}}(self, body: dict, __user__: Optional[dict] = None, __event_emitter__=None, __event_call__=None, __request__: Optional[Request] = None) -> dict:
if self.valves.show_status and __event_emitter__:
user_ctx = await self._get_user_context(__user__, __event_call__, __request__)
msg = self._get_translation(user_ctx["user_language"], "status_starting")
await __event_emitter__({"type": "status", "data": {"description": msg, "done": False}})
# Implement core logic here
if self.valves.show_status and __event_emitter__:
await __event_emitter__({"type": "status", "data": {"description": "Done", "done": True}})
return body

View File

@@ -0,0 +1,80 @@
"""
title: {{TITLE}}
author: Fu-Jie
author_url: https://github.com/Fu-Jie/openwebui-extensions
funding_url: https://github.com/open-webui
version: 0.1.0
description: {{DESCRIPTION}}
"""
import asyncio
import logging
import json
from typing import Optional, Dict, Any, List, Callable, Awaitable
from pydantic import BaseModel, Field
from fastapi import Request
# Configure logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
logger = logging.getLogger(__name__)
TRANSLATIONS = {
"en-US": {"status_starting": "Starting {{TITLE}}..."},
"zh-CN": {"status_starting": "正在启动 {{TITLE}}..."},
"zh-HK": {"status_starting": "正在啟動 {{TITLE}}..."},
"zh-TW": {"status_starting": "正在啟動 {{TITLE}}..."},
"ko-KR": {"status_starting": "{{TITLE}} 시작 중..."},
"ja-JP": {"status_starting": "{{TITLE}} を起動中..."},
"fr-FR": {"status_starting": "Démarrage de {{TITLE}}..."},
"de-DE": {"status_starting": "{{TITLE}} wird gestartet..."},
"es-ES": {"status_starting": "Iniciando {{TITLE}}..."},
"it-IT": {"status_starting": "Avvio di {{TITLE}}..."},
"vi-VN": {"status_starting": "Đang khởi động {{TITLE}}..."},
"id-ID": {"status_starting": "Memulai {{TITLE}}..."},
}
class {{CLASS_NAME}}:
class Valves(BaseModel):
priority: int = Field(default=50, description="Priority level (lower = earlier).")
show_status: bool = Field(default=True, description="Show status updates in UI.")
def __init__(self):
self.valves = self.Valves()
self.fallback_map = {
"zh": "zh-CN", "en": "en-US", "ko": "ko-KR", "ja": "ja-JP",
"fr": "fr-FR", "de": "de-DE", "es": "es-ES", "it": "it-IT",
"vi": "vi-VN", "id": "id-ID"
}
def _get_translation(self, lang: str, key: str, **kwargs) -> str:
target_lang = lang
if target_lang not in TRANSLATIONS:
base = target_lang.split("-")[0]
target_lang = self.fallback_map.get(base, "en-US")
lang_dict = TRANSLATIONS.get(target_lang, TRANSLATIONS["en-US"])
text = lang_dict.get(key, TRANSLATIONS["en-US"].get(key, key))
return text.format(**kwargs) if kwargs else text
async def _get_user_context(self, __user__: Optional[dict], __event_call__: Optional[Callable] = None, __request__: Optional[Request] = None) -> dict:
user_data = __user__ if isinstance(__user__, dict) else {}
user_language = user_data.get("language", "en-US")
if __event_call__:
try:
js = "try { return (document.documentElement.lang || localStorage.getItem('locale') || navigator.language || 'en-US'); } catch (e) { return 'en-US'; }"
frontend_lang = await asyncio.wait_for(__event_call__({"type": "execute", "data": {"code": js}}), timeout=2.0)
if frontend_lang: user_language = frontend_lang
except: pass
return {"user_language": user_language}
async def {{METHOD_NAME}}(self, body: dict, __user__: Optional[dict] = None, __event_emitter__=None, __event_call__=None, __request__: Optional[Request] = None) -> dict:
if self.valves.show_status and __event_emitter__:
user_ctx = await self._get_user_context(__user__, __event_call__, __request__)
msg = self._get_translation(user_ctx["user_language"], "status_starting")
await __event_emitter__({"type": "status", "data": {"description": msg, "done": False}})
# Implement core logic here
if self.valves.show_status and __event_emitter__:
await __event_emitter__({"type": "status", "data": {"description": "Done", "done": True}})
return body

View File

@@ -0,0 +1,66 @@
#!/usr/bin/env python3
import sys
import os
def scaffold(p_type, p_name, title, desc):
target_dir = f"plugins/{p_type}/{p_name}"
os.makedirs(target_dir, exist_ok=True)
class_name = (
"Action"
if p_type == "actions"
else (
"Filter"
if p_type == "filters"
else "Tools" if p_type == "tools" else "Pipe"
)
)
method_name = (
"action"
if p_type == "actions"
else (
"outlet"
if p_type == "filters"
else "execute" if p_type == "tools" else "pipe"
)
)
replacements = {
"{{TITLE}}": title,
"{{DESCRIPTION}}": desc,
"{{CLASS_NAME}}": class_name,
"{{METHOD_NAME}}": method_name,
}
# Files to generate
templates = [
("assets/template.py.j2", f"{p_name}.py"),
("assets/README_template.md", "README.md"),
("assets/README_template.md", "README_CN.md"),
]
# Path relative to skill root
skill_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
for t_path, t_name in templates:
template_file = os.path.join(skill_root, t_path)
if not os.path.exists(template_file):
print(f"⚠️ Warning: Template not found {template_file}")
continue
with open(template_file, "r") as f:
content = f.read()
for k, v in replacements.items():
content = content.replace(k, v)
with open(os.path.join(target_dir, t_name), "w") as f:
f.write(content)
print(f"✅ Generated: {target_dir}/{t_name}")
if __name__ == "__main__":
if len(sys.argv) < 5:
print("Usage: scaffold.py <type> <name> <title> <desc>")
sys.exit(1)
scaffold(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4])

View File

@@ -0,0 +1,180 @@
---
name: pr-reviewer
description: Fetches PR review comments, analyzes requested changes, implements fixes, commits and pushes the resolution. Use after a reviewer has left comments on an open PR to close the feedback loop efficiently.
---
# PR Reviewer
## Overview
This skill automates the response cycle for code review. When a reviewer leaves comments on a Pull Request, this skill fetches all pending feedback, categorizes issues by severity, implements fixes, and submits a follow-up commit with appropriate review response comments.
## Prerequisites
- An open PR exists with pending review comments
- The local branch matches the PR's head branch
- `gh` CLI is authenticated
---
## Workflow
### Step 1 — Fetch Review State
Retrieve all review comments and overall review status:
```bash
# Get overall review decisions
PAGER=cat GH_PAGER=cat gh pr view <PR-NUMBER> --json reviews,reviewDecision,headRefName \
--jq '{decision: .reviewDecision, reviews: [.reviews[] | {author: .author.login, state: .state, body: .body}]}'
# Get inline code comments (specific line comments)
PAGER=cat GH_PAGER=cat gh api repos/Fu-Jie/openwebui-extensions/pulls/<PR-NUMBER>/comments \
--jq '[.[] | {path: .path, line: .line, body: .body, author: .user.login, id: .id}]'
# Get general issue comments
PAGER=cat GH_PAGER=cat gh issue view <PR-NUMBER> --comments --json comments \
--jq '[.comments[] | {author: .author.login, body: .body}]'
```
Confirm the current local branch matches the PR head:
```bash
git branch --show-current
```
If mismatched, checkout the correct branch first.
### Step 2 — Categorize Review Feedback
Group feedback into categories:
| Category | Examples | Action |
|----------|---------|--------|
| **Code Bug** | Logic error, incorrect variable, broken condition | Fix code immediately |
| **Style / Formatting** | Indentation, naming convention, missing blank line | Fix code |
| **Documentation** | Missing i18n key, wrong version in README, typo | Fix docs |
| **Design Question** | Suggestion to restructure, alternative approach | Discuss with user before implementing |
| **Nitpick / Optional** | Minor style preferences reviewer marked as optional | Fix if quick; document if skipped |
| **Blocking** | Reviewer explicitly blocks merge | Must fix before proceeding |
Present the full categorized list to the user and confirm the resolution plan.
### Step 3 — Implement Fixes
For each accepted fix:
1. Read the affected file at the commented line for context:
```bash
sed -n '<line-5>,<line+10>p' <file-path>
```
2. Apply the fix using appropriate file edit tools
3. After editing, verify the specific area looks correct
**For code changes that might affect behavior:**
- Check if tests exist: `ls tests/test_*.py`
- If tests exist, run them: `python -m pytest tests/ -v`
**For documentation fixes:**
- If modifying README.md, check if `docs/` mirror needs the same fix
- Apply the same fix to both locations
### Step 4 — Run Consistency Checks
After all fixes are applied:
```bash
# Version consistency (if any version files were touched)
python3 scripts/check_version_consistency.py
# Quick syntax check for Python files
python3 -m py_compile plugins/{type}/{name}/{name}.py && echo "✅ Syntax OK"
```
### Step 5 — Stage and Commit
Create a new commit (do NOT amend if the branch has already been pushed, to avoid force-push):
```bash
git add -A
git status
```
Draft a Conventional Commits message for the fixup:
Format: `fix(scope): address review feedback`
Body should list what was fixed, referencing reviewer concerns:
```
fix(github-copilot-sdk): address review feedback from @reviewer
- Fix X per review comment on line Y of file Z
- Update README to clarify auth requirement
- Correct edge case in _parse_mcp_servers logic
```
```bash
git commit -m "<fixup commit message>"
```
### Step 6 — Push the Fix Commit
```bash
git push origin $(git branch --show-current)
```
**Force-push policy:**
- Use `git push` (non-force) by default
- Only use `git push --force-with-lease` if:
1. The user explicitly requests it, AND
2. The only change is an amended commit squash (cosmetic, no logic change)
3. Never use `--force` (without `--lease`)
### Step 7 — Respond to Reviewers
For each addressed review comment, post a reply:
```bash
# Reply to inline comment
gh api repos/Fu-Jie/openwebui-extensions/pulls/<PR-NUMBER>/comments/<COMMENT-ID>/replies \
-X POST -f body="Fixed in commit <SHORT-SHA>. <Brief explanation of what was changed.>"
# General comment to summarize all fixes
gh issue comment <PR-NUMBER> --body "All review feedback addressed in commit <SHORT-SHA>:
- Fixed: <item 1>
- Fixed: <item 2>
Ready for re-review. 🙏"
```
### Step 8 — Re-Request Review (Optional)
If the reviewer had submitted a `CHANGES_REQUESTED` review, request a new review after fixes:
```bash
PAGER=cat GH_PAGER=cat gh api repos/Fu-Jie/openwebui-extensions/pulls/<PR-NUMBER>/requested_reviewers \
-X POST -f reviewers[]='<reviewer-login>'
```
---
## Decision Guide
### When NOT to implement a suggestion immediately
- **Design questions**: "Should this be a separate class?" — Present to user for decision
- **Optional nitpicks**: Reviewer marked as `nit:` — Ask user if they want to include it
- **Large refactors**: If fix would require changing >50 lines, propose a separate follow-up issue instead
### When to ask the user before proceeding
- Any fix involving behavioral changes to plugin logic
- Renaming Valve keys (breaking change — requires migration notes)
- Changes that affect the bilingual release notes already committed
---
## Anti-Patterns to Avoid
- ❌ Do NOT `git commit --amend` on a pushed commit without user approval for force-push
- ❌ Do NOT silently skip a reviewer's comment; always acknowledge it (implement or explain why not)
- ❌ Do NOT use `--force` (only `--force-with-lease` when absolutely necessary)
- ❌ Do NOT make unrelated changes in the fixup commit; keep scope focused on review feedback
- ❌ Do NOT respond to reviewer comments in Chinese if the PR language context is English

View File

@@ -0,0 +1,194 @@
---
name: pr-submitter
description: Submits a feature branch as a Pull Request with a validated, properly formatted bilingual PR body. Handles shell-escape-safe body writing via temp files. Use after release-prep has committed all changes.
---
# PR Submitter
## Overview
This skill handles the final step of pushing a feature branch and creating a validated Pull Request on GitHub. Its primary purpose is to avoid the shell-escaping pitfalls (backticks, special characters in `gh pr create --body`) by always writing the PR body to a **temp file** first.
## Prerequisites
- All changes are committed (use `release-prep` skill first)
- The `gh` CLI is authenticated (`gh auth status`)
- Current branch is NOT `main` or `master`
---
## Workflow
### Step 0 — Initialize Temp Directory (Project-Based)
For all temporary files, use the project's `.temp/` directory instead of system `/tmp`:
```bash
# Create temp directory if it doesn't exist
mkdir -p .temp
```
**Why**: All temporary files stay within the project workspace, avoiding system `/tmp` pollution and better aligning with OpenWebUI workspace isolation principles.
### Step 1 — Pre-Flight Checks
Run these checks before any push:
```bash
# 1. Confirm not on protected branch
git branch --show-current
# 2. Verify there are commits to push
git log origin/$(git branch --show-current)..HEAD --oneline 2>/dev/null || echo "No remote tracking branch yet"
# 3. Check gh CLI auth
gh auth status
```
If any check fails, stop and report clearly.
### Step 2 — Collect PR Metadata
Gather:
- **PR Title**: Must follow Conventional Commits format, English only (e.g., `feat(github-copilot-sdk): release v0.8.0 with conditional tool filtering`)
- **Target base branch**: Default is `main`
- **Plugin name + version** (to build body sections)
- **Key changes** (reuse from release-prep or the latest What's New section)
### Step 3 — Build PR Body File (Shell-Escape-Safe)
**Always write the body to a temp file in `.temp/` directory.** Never embed multi-line markdown with special characters directly in a shell command.
```bash
cat > .temp/pr_body.md << 'HEREDOC'
## Summary
Brief one-sentence description of what this PR accomplishes.
## Changes
### New Features
- Feature 1 description
- Feature 2 description
### Bug Fixes
- Fix 1 description
## Plugin Version
- `PluginName` bumped to `vX.X.X`
## Documentation
- README.md / README_CN.md updated
- docs/ mirrors synced
## Testing
- [ ] Tested locally in OpenWebUI
- [ ] i18n validated (all language keys present)
- [ ] Version consistency check passed (`python3 scripts/check_version_consistency.py`)
---
## 变更摘要(中文)
简要描述本次 PR 的改动内容。
### 新功能
- 功能1描述
- 功能2描述
### 问题修复
- 修复1描述
HEREDOC
```
**Critical rules for the body file:**
- Use `<< 'HEREDOC'` (quoted heredoc) to prevent variable expansion
- Keep all backticks literal — they are safe inside a heredoc
- Paths like `/api/v1/files/` are safe too since heredoc doesn't interpret them as commands
### Step 4 — Validate PR Body
Before submitting, verify the body file contains expected sections:
```bash
# Check key sections exist
grep -q "## Summary" .temp/pr_body.md && echo "✅ Summary" || echo "❌ Summary missing"
grep -q "## Changes" .temp/pr_body.md && echo "✅ Changes" || echo "❌ Changes missing"
grep -q "## 变更摘要" .temp/pr_body.md && echo "✅ CN Section" || echo "❌ CN Section missing"
# Preview the body
cat .temp/pr_body.md
```
Ask the user to confirm the body content before proceeding.
### Step 5 — Push Branch
```bash
git push -u origin $(git branch --show-current)
```
If push is rejected (non-fast-forward), report to user and ask whether to force-push. **Do NOT force-push without explicit confirmation.**
### Step 6 — Create Pull Request
```bash
gh pr create \
--base main \
--head $(git branch --show-current) \
--title "<PR title from Step 2>" \
--body-file .temp/pr_body.md
```
Always use `--body-file`, never `--body` with inline markdown.
### Step 7 — Verify PR Creation
```bash
PAGER=cat GH_PAGER=cat gh pr view --json number,url,title,body --jq '{number: .number, url: .url, title: .title, body_preview: .body[:200]}'
```
Confirm:
- PR number and URL
- Title matches intended Conventional Commits format
- Body preview includes key sections (not truncated/corrupted)
If the body appears corrupted (empty sections, missing backtick content), use edit:
```bash
gh pr edit <PR-NUMBER> --body-file /tmp/pr_body.md
```
### Step 8 — Cleanup
```bash
rm -f .temp/pr_body.md
```
**Note**: The `.temp/` directory itself is preserved for reuse; only the individual PR body file is deleted. To fully clean up: `rm -rf .temp/`
Report final PR URL to the user.
---
## Shell-Escape Safety Rules
| Risk | Safe Approach |
|------|--------------|
| Backticks in `--body` | Write to file, use `--body-file` |
| Paths like `/api/...` | Safe in heredoc; risky in inline `--body` |
| Newlines in `--body` | File-based only |
| `$variable` expansion | Use `<< 'HEREDOC'` (quoted) |
| Double quotes in body | Safe in heredoc file |
| Temp file storage | Use `.temp/` dir, not `/tmp` |
| Cleanup after use | Always delete temp file (keep dir) |
---
## Anti-Patterns to Avoid
- ❌ Never use `--body "..."` with multi-line content directly in shell command
- ❌ Never interpolate variables directly into heredoc without quoting the delimiter
- ❌ Never force-push (`--force`) without explicit user confirmation
- ❌ Never target `main` as the source branch (only as base)
- ❌ Never skip the body validation step — a PR with empty body is worse than a delayed PR

View File

@@ -0,0 +1,208 @@
---
name: release-finalizer
description: Merges a release PR, associates it with resolved issues, replies to issue reporters, and closes issues. Use after PR review is complete and ready for merge. Closes the release cycle.
---
# Release Finalizer
## Overview
This skill completes the final step of the release cycle: merging the release PR to `main`, replying to all related issues with solutions, and automatically closing them using GitHub's issue linking mechanism.
## Prerequisites
- The PR is in `OPEN` state and ready to merge
- All status checks have passed (CI green)
- All review feedback has been addressed
- The PR relates to one or more GitHub issues (either in PR description or through commits)
---
## Workflow
### Step 1 — Pre-Merge Verification
Verify that the PR is ready:
```bash
PAGER=cat GH_PAGER=cat gh pr view <PR-NUMBER> --json state,statusCheckRollup,reviewDecision
```
Checklist:
-`state` is `OPEN`
-`statusCheckRollup` all have `conclusion: SUCCESS`
-`reviewDecision` is `APPROVED` or empty (no blocking reviews)
If any check fails, **do NOT merge**. Report the issue to the user.
### Step 2 — Identify Related Issues
Issues can be linked to a PR in multiple ways. Check the PR description and commit messages for keywords:
```bash
PAGER=cat GH_PAGER=cat gh pr view <PR-NUMBER> --json body,commits
```
Look for patterns like:
- `Closes #XX`, `Fixes #XX`, `Resolves #XX` (in description or commit bodies)
- `#XX` mentioned as "related to" or "addresses"
**Manual input**: If issue links are not in the PR, ask the user which issue(s) this PR resolves.
Extract all issue numbers into a list: `[#48, #52, ...]`
### Step 3 — Select Merge Strategy
Offer the user three options:
| Strategy | Git Behavior | Use Case |
|----------|-------------|----------|
| **Squash** | All commits squashed into one commit on main | Clean history, recommended for release PRs |
| **Rebase** | Linear history, no merge commit | Preserve commit granularity |
| **Merge** | Merge commit created | Preserve full PR context |
**Recommendation for release PRs**: Use `--squash` to create a single clean commit.
If user doesn't specify, default to `--squash`.
### Step 4 — Prepare Merge Commit Message
If using `--squash`, craft a single comprehensive commit message:
**Format** (Conventional Commits + Github linking):
```
type(scope): description
- Bullet point 1
- Bullet point 2
Closes #48
Closes #52
```
The `Closes #XX` keyword tells GitHub to automatically close those issues when the commit lands on `main`.
Example:
```
feat(pipes,filters): release Copilot SDK Pipe v0.8.0 and Files Filter v0.1.3
- Implement P1~P4 conditional tool filtering system
- Fix file publishing reliability across all storage backends
- Add strict file URL validation
- Update bilingual documentation
Closes #48
```
### Step 5 — Execute Merge
```bash
gh pr merge <PR-NUMBER> \
--squash \
--delete-branch \
-m "type(scope): description" \
-b "- Bullet 1\n- Bullet 2\n\nCloses #48"
```
**Key flags:**
- `--squash`: Squash commits (recommended for releases)
- `--delete-branch`: Delete the feature branch after merge
- `-m`: Commit subject
- `-b`: Commit body (supports `\n` for newlines)
Confirm the merge is successful; GitHub will automatically close related issues with `Closes #XX` keyword.
### Step 6 — Verify Auto-Close
GitHub automatically closes issues when a commit with `Closes #XX` lands on the default branch (`main`).
To verify:
```bash
PAGER=cat GH_PAGER=cat gh issue view <ISSUE-NUMBER> --json state
```
Should show `state: CLOSED`.
### Step 7 — Post Closing Message (Optional but Recommended)
For better UX, manually post a summary comment to **each issue** before it auto-closes (since auto-close happens silently):
```bash
gh issue comment <ISSUE-NUMBER> --body "
This has been fixed in PR #<PR-NUMBER>, which is now merged to main.
**Solution Summary:**
- <Key fix 1>
- <Key fix 2>
The fix will be available in the next plugin release. Thank you for reporting! ⭐
"
```
### Step 8 — (Optional) Regenerate Release Notes
If the merge revealed any final tweaks to release notes:
```bash
# Re-export release notes from merged commit
git log --oneline -1 <merged-commit-sha>
```
If needed, create a follow-up PR with doc polish (do NOT force-push the merged commit).
---
## Merge Strategy Decision Tree
```
Is this a patch/hotfix release?
├─ YES → Use --squash
└─ NO → Multi-feature release?
├─ YES → Use --squash (cleaner history)
└─ NO → Preserve detail?
├─ YES → Use --rebase
└─ NO → Use --merge (preserve PR context)
```
---
## Issue Auto-Close Keywords
These keywords in commit/PR messages will auto-close issues when merged to `main`:
- `Closes #XX`
- `Fixes #XX`
- `Resolves #XX`
- `close #XX` (case-insensitive)
- `fix #XX`
- `resolve #XX`
**Important**: The keyword must be on the **final commit that lands on** `main`. For squash merges, it must be in the squash commit message body.
---
## Anti-Patterns to Avoid
- ❌ Do NOT merge if any status checks are PENDING or FAILED
- ❌ Do NOT merge if there are blocking reviews (reviewDecision: `CHANGES_REQUESTED`)
- ❌ Do NOT merge without verifying the Conventional Commits format in the merge message
- ❌ Do NOT merge without including `Closes #XX` keywords for all related issues
- ❌ Do NOT assume issues will auto-close silently — post a courtesy comment first
- ❌ Do NOT delete the branch if it might be needed for cherry-pick or hotfixes later
---
## Troubleshooting
### Issue did not auto-close after merge
- Verify the `Closes #XX` keyword is in the **final commit message** (use `git log` to check)
- Ensure the commit is on the `main` branch
- GitHub sometimes takes a few seconds to process; refresh the issue page
### Multiple issues to close
- List all in separate `Closes #XX` lines in the commit body
- Each one will be independently auto-closed
### Want to close issue without merge?
- Use `gh issue close <ISSUE-NUMBER>` manually
- Only recommended if the PR was manually reverted or deemed invalid

View File

@@ -0,0 +1,157 @@
---
name: release-prep
description: Orchestrates the full release preparation flow for a plugin — version sync across 7+ files, bilingual release notes creation, and commit message drafting. Use before submitting a PR. Does NOT push or create a PR; that is handled by pr-submitter.
---
# Release Prep
## Overview
This skill drives the complete pre-PR release pipeline. It enforces the repository rule that every release must synchronize the version number and changelog across **at least 7 locations** before a commit is created.
## Scope
This skill covers:
1. Version sync (delegates detail to `version-bumper` if needed)
2. Bilingual release notes file creation
3. 7-location consistency verification
4. Conventional Commits message drafting
5. `git add -A && git commit` execution
It **stops before** `git push` or `gh pr create`. Use the `pr-submitter` skill for those steps.
### Temporary File Convention
Any temporary files created during release prep (e.g., draft changelogs) must:
- Be written to the project's `.temp/` directory, **NOT** system `/tmp`
- Be cleaned up before commit using `rm -f .temp/file_name`
- Never be committed to git (add `.temp/` to `.gitignore`)
---
## Workflow
### Step 1 — Collect Release Info
Ask the user (or infer from current state) the following:
- **Plugin name** and **type** (actions / filters / pipes / tools)
- **New version number** (e.g., `0.8.0`)
- **Key changes** in English and Chinese (1-5 bullet points each)
If a `What's New` section already exists in README.md, extract it as the source of truth.
### Step 2 — Sync Version Across 7 Locations
Verify AND update the version string in all of the following. Mark each as ✅ or ❌:
| # | File | Location |
|---|------|----------|
| 1 | `plugins/{type}/{name}/{name}.py` | `version:` in docstring |
| 2 | `plugins/{type}/{name}/README.md` | `**Version:** x.x.x` metadata line |
| 3 | `plugins/{type}/{name}/README_CN.md` | `**Version:** x.x.x` metadata line |
| 4 | `docs/plugins/{type}/{name}.md` | `**Version:** x.x.x` metadata line |
| 5 | `docs/plugins/{type}/{name}.zh.md` | `**Version:** x.x.x` metadata line |
| 6 | `docs/plugins/{type}/index.md` | version badge for this plugin |
| 7 | `docs/plugins/{type}/index.zh.md` | version badge for this plugin |
Additionally update the root-level **updated date badge** in:
- `README.md``![updated](https://img.shields.io/badge/YYYY--MM--DD-gray?style=flat)`
- `README_CN.md` — same badge format
Use today's date (`YYYY-MM-DD`) for the badge.
### Step 3 — Update What's New (All 4 Doc Files)
The `What's New` / `最新更新` section must contain **only the most recent release's changes**. Previous entries should be removed from this section (they live in CHANGELOG or release notes files).
Update these 4 files' `What's New` / `最新更新` block consistently:
- `plugins/{type}/{name}/README.md`
- `plugins/{type}/{name}/README_CN.md`
- `docs/plugins/{type}/{name}.md`
- `docs/plugins/{type}/{name}.zh.md`
### Step 4 — Create Bilingual Release Notes Files
Create two versioned release notes files:
**Path**: `plugins/{type}/{name}/v{version}.md`
**Path**: `plugins/{type}/{name}/v{version}_CN.md`
#### Required Sections
Each file must include:
1. **Title**: `# v{version} Release Notes` (EN) / `# v{version} 版本发布说明` (CN)
2. **Overview**: One paragraph summarizing this release
3. **New Features** / **新功能**: Bulleted list of features
4. **Bug Fixes** / **问题修复**: Bulleted list of fixes
5. **Migration Notes** / **迁移说明**: Breaking changes or Valve key renames (omit section if none)
6. **Companion Plugins** / **配套插件** (optional): If a companion plugin was updated
If a release notes file already exists for this version, update it rather than creating a new one.
#### Full Coverage Rule (Mandatory)
Release notes must cover **all updates in the current release scope** and not only headline features.
Minimum required coverage in both EN/CN files:
- New features and capability enhancements
- Bug fixes and reliability fixes
- Documentation/README/doc-mirror updates that affect user understanding or usage
- Terminology/i18n/wording fixes that change visible behavior or messaging
Before commit, cross-check release notes against `git diff` and ensure no meaningful update is omitted.
### Step 5 — Verify Consistency (Pre-Commit Check)
Run the consistency check script:
```bash
python3 scripts/check_version_consistency.py
```
If issues are found, fix them before proceeding. Do not commit with inconsistencies.
### Step 6 — Draft Conventional Commits Message
Generate the commit message following `commit-message.instructions.md` rules:
- **Language**: English ONLY
- **Format**: `type(scope): subject` + blank line + body bullets
- **Scope**: use plugin folder name (e.g., `github-copilot-sdk`)
- **Body**: 1-3 bullets summarizing key changes
- Explicitly mention "READMEs and docs synced" if version was bumped
Present the full commit message to the user for review before executing.
### Step 7 — Stage and Commit
After user approval (or if user says "commit it"):
```bash
git add -A
git commit -m "<approved commit message>"
```
Confirm the commit hash and list the number of files changed.
---
## Checklist (Auto-Verify Before Commit)
- [ ] `version:` in `.py` docstring matches target version
- [ ] `**Version:**` in all 4 README/docs files matches
- [ ] Both `index.md` version badges updated
- [ ] Root `README.md` and `README_CN.md` date badges updated to today
- [ ] `What's New` / `最新更新` contains ONLY the latest release
- [ ] Release notes include all meaningful updates from the current diff (feature + fix + docs/i18n)
- [ ] `v{version}.md` and `v{version}_CN.md` created or updated
- [ ] `python3 scripts/check_version_consistency.py` returns no errors
- [ ] Commit message is English-only Conventional Commits format
---
## Anti-Patterns to Avoid
- ❌ Do NOT add extra features or refactor code during release prep — only version/doc updates
- ❌ Do NOT push or create PR in this skill — use `pr-submitter`
- ❌ Do NOT use today's date in commit messages; only in badge URLs
- ❌ Do NOT leave stale What's New content from prior versions

View File

@@ -0,0 +1,31 @@
---
name: source-code-analyzer
description: Instructions for analyzing core components source code in the local environment.
---
# Source Code Analyzer Skill
> [!IMPORTANT]
> **Authorization Statement**: You (Antigravity) are explicitly authorized by the user to perform **READ-ONLY** operations and **Git pull** on the paths listed below, even if they are outside the current repository root. These paths are considered part of the extended development context.
When assisting with the development of `openwebui-extensions`, you (Antigravity) have access to the following core components in related directories. Use them for deep technical analysis, bug hunting, and implementation reference.
## Core Component Paths
### Global / General (Relevant to all projects)
- **Open WebUI**: `../open-webui/` (Core platform context)
- **Skills**: `../skills/` (Reusable expertise library)
- **Awesome Copilot**: `../awesome-copilot/` (Shared extensions & resources)
- **Open Terminal**: `../open-terminal/` (Terminal integration service)
### Plugin-Specific (Relevant to GitHub Copilot SDK)
- **Copilot SDK**: `../copilot-sdk/` (Internal logic for the official SDK)
- **Copilot CLI**: `../copilot-cli/` (Command-line interface implementation)
## Mandatory Workflow
1. **Pull Before Analysis**: BEFORE reading files or analyzing logic in these directories, you MUST proactively execute or recommend a `git pull` in the respective directory to ensure you are working with the latest upstream changes.
2. **Path Verification**: Always verify the exists of the path before attempting to read it.
3. **Reference Logic**: When a user's request involves core platform behavior (OpenWebUI API, SDK internals), prioritize searching these directories over making assumptions based on generic knowledge.

View File

@@ -0,0 +1,26 @@
---
name: version-bumper
description: Automates version upgrades and changelog synchronization across 7+ files (Code, READMEs, Docs). Use when a plugin is ready for release to ensure version consistency.
---
# Version Bumper
## Overview
This skill ensures that every version upgrade is synchronized across the entire repository, following the strict "Documentation Sync" rule in GEMINI.md.
## Workflow
1. **Prepare Info**: Gather the new version number and brief changelogs in both English and Chinese.
2. **Auto-Patch**: The skill will help you identify and update:
- `plugins/.../name.py` (docstring version)
- `plugins/.../README.md` (metadata & What's New)
- `plugins/.../README_CN.md` (metadata & 最新更新)
- `docs/plugins/...md` (mirrors)
- `docs/plugins/index.md` (version badge)
- `README.md` (updated date badge)
3. **Verify**: Check the diffs to ensure no formatting was broken.
## Tool Integration
Execute the bump script (draft):
```bash
python3 scripts/bump.py <version> "<message_en>" "<message_zh>"
```

View File

@@ -0,0 +1,70 @@
#!/usr/bin/env python3
import sys
import os
import re
from datetime import datetime
def patch_file(file_path, old_pattern, new_content, is_regex=False):
if not os.path.exists(file_path):
print(f"Warning: File not found: {file_path}")
return False
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
if is_regex:
new_content_result = re.sub(old_pattern, new_content, content, flags=re.MULTILINE)
else:
new_content_result = content.replace(old_pattern, new_content)
if new_content_result != content:
with open(file_path, 'w', encoding='utf-8') as f:
f.write(new_content_result)
print(f"✅ Patched: {file_path}")
return True
else:
print(f" No change needed: {file_path}")
return False
def bump_version(plugin_type, plugin_name, new_version, msg_en, msg_zh):
print(f"🚀 Bumping {plugin_name} ({plugin_type}) to {new_version}...")
today = datetime.now().strftime("%Y-%m-%d")
today_badge = today.replace("-", "--")
# 1. Patch Plugin Python File
py_file = f"plugins/{plugin_type}/{plugin_name}/{plugin_name}.py"
patch_file(py_file, r"version: \d+\.\d+\.\d+", f"version: {new_version}", is_regex=True)
# 2. Patch Plugin READMEs
readme_en = f"plugins/{plugin_type}/{plugin_name}/README.md"
readme_zh = f"plugins/{plugin_type}/{plugin_name}/README_CN.md"
# Update version in metadata
patch_file(readme_en, r"\*\*Version:\*\* \d+\.\d+\.\d+", f"**Version:** {new_version}", is_regex=True)
patch_file(readme_zh, r"\*\*版本:\*\* \d+\.\d+\.\d+", f"**版本:** {new_version}", is_regex=True)
# Update What's New (Assuming standard headers)
patch_file(readme_en, r"## 🔥 What's New in v.*?\n", f"## 🔥 What's New in v{new_version}\n\n* {msg_en}\n", is_regex=True)
patch_file(readme_zh, r"## 🔥 最新更新 v.*?\n", f"## 🔥 最新更新 v{new_version}\n\n* {msg_zh}\n", is_regex=True)
# 3. Patch Docs Mirrors
doc_en = f"docs/plugins/{plugin_type}/{plugin_name}.md"
doc_zh = f"docs/plugins/{plugin_type}/{plugin_name}.zh.md"
patch_file(doc_en, r"\*\*Version:\*\* \d+\.\d+\.\d+", f"**Version:** {new_version}", is_regex=True)
patch_file(doc_zh, r"\*\*版本:\*\* \d+\.\d+\.\d+", f"**版本:** {new_version}", is_regex=True)
# 4. Patch Root READMEs (Updated Date Badge)
patch_file("README.md", r"badge/202\d--\d\d--\d\d-gray", f"badge/{today_badge}-gray", is_regex=True)
patch_file("README_CN.md", r"badge/202\d--\d\d--\d\d-gray", f"badge/{today_badge}-gray", is_regex=True)
print("\n✨ All synchronization tasks completed.")
return True
if __name__ == "__main__":
if len(sys.argv) < 6:
print("Usage: bump.py <type> <name> <version> <msg_en> <msg_zh>")
print("Example: bump.py filters markdown_normalizer 1.2.8 'Fix bug' '修复错误'")
sys.exit(1)
bump_version(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5])

View File

@@ -140,6 +140,7 @@ Before committing:
- [ ] `docs/` index and detail pages are updated?
- [ ] Root `README.md` is updated?
- [ ] All version numbers match exactly?
- [ ] Any non-obvious findings saved to `.agent/learnings/{topic}.md`?
## 5. Git Operations (Agent Rules)
@@ -147,3 +148,12 @@ Before committing:
2. **No Auto-Commit**: Never `git commit`, `git push`, or `create_pull_request` automatically after file updates unless the user explicitly says "commit this" or "release now".
3. **Draft Mode**: If available, use PRs as drafts first.
4. **Reference**: Strictly follow the rules defined in `.github/copilot-instructions.md`**Git Operations (Agent Rules)** section.
## 6. Knowledge Capture (Mandatory)
Whenever you discover a non-obvious behaviour, internal API contract, or workaround
during plugin development, **document it in `.agent/learnings/{topic}.md`** before
ending the session.
- Browse `.agent/learnings/` **first** at the start of a session to reuse existing knowledge.
- Format: see `.agent/learnings/README.md`.

View File

@@ -73,11 +73,13 @@ Create two versioned release notes files:
#### Required Sections
Each file must include:
1. **Title**: `# v{version} Release Notes` (EN) / `# v{version} 版本发布说明` (CN)
2. **Overview**: One paragraph summarizing this release
3. **New Features** / **新功能**: Bulleted list of features
4. **Bug Fixes** / **问题修复**: Bulleted list of fixes
5. **Migration Notes** / **迁移说明**: Breaking changes or Valve key renames (omit section if none)
0. **Marketplace Link**: Direct link to the plugin on openwebui.com (e.g., `**[🚀 Get/Update on OpenWebUI Community](URL)**`)
1. **Overview**: One paragraph summarizing this release
2. **New Features** / **新功能**: Bulleted list of features
3. **Bug Fixes** / **问题修复**: Bulleted list of fixes
4. **Related Issues** / **相关 Issue**: Link to the GitHub Issue(s) resolved in this release (e.g., `**[#56](URL)**`). MANDATORY if the release resolves a reported issue.
5. **Related PRs** / **相关 PR**: Link to the Pull Request(s) associated with this release. (e.g., `**[#123](URL)**`). MANDATORY if the release is being prepared within an existing PR.
6. **Migration Notes** / **迁移说明**: Breaking changes or Valve key renames (omit section if none)
6. **Companion Plugins** / **配套插件** (optional): If a companion plugin was updated
If a release notes file already exists for this version, update it rather than creating a new one.
@@ -98,8 +100,10 @@ Generate the commit message following `commit-message.instructions.md` rules:
- **Language**: English ONLY
- **Format**: `type(scope): subject` + blank line + body bullets
- **Scope**: use plugin folder name (e.g., `github-copilot-sdk`)
- **Body**: 1-3 bullets summarizing key changes
- Explicitly mention "READMEs and docs synced" if version was bumped
- **Body**:
- 1-3 bullets summarizing key changes
- Explicitly mention "READMEs and docs synced" if version was bumped
- **MUST** end with `Closes #XX` or `Fixes #XX` if an issue is being resolved.
Present the full commit message to the user for review before executing.

165
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,165 @@
name: 🐛 Bug Report
description: Report a bug or issue with OpenWebUI plugins
title: "[BUG] "
labels: ["bug"]
assignees: []
body:
- type: markdown
attributes:
value: |
Thanks for reporting a bug! Please provide clear information to help us reproduce and fix the issue.
- type: dropdown
id: plugin-type
attributes:
label: Plugin Type
description: Which type of plugin is affected?
options:
- Action
- Filter
- Pipe
- Pipeline
- Tool
- Other
validations:
required: true
- type: dropdown
id: plugin-name
attributes:
label: Plugin Name
description: Which plugin has the issue?
options:
- "Select a plugin..."
- "Action - Deep Dive"
- "Action - Export to Word Enhanced"
- "Action - Export to Excel"
- "Action - Flash Card"
- "Action - Smart Infographic"
- "Action - Smart Mind Map"
- "Filter - Async Context Compression"
- "Filter - Context & Model Enhancement Filter"
- "Filter - Folder Memory"
- "Filter - GitHub Copilot SDK Files Filter"
- "Filter - Markdown Normalizer"
- "Filter - Gemini Multimodel Filter"
- "Pipeline - MOE Prompt Refiner"
- "Pipe - GitHub Copilot SDK"
- "Pipe - iFlow SDK"
- "Tool - OpenWebUI Skills Manager"
- "Tool - Smart Infographic Tool"
- "Tool - Smart Mind Map Tool"
- "Other / Not Listed"
validations:
required: true
- type: input
id: plugin-name-other
attributes:
label: Plugin Name (if not in list)
description: If you selected 'Other / Not Listed', please specify the plugin name
placeholder: "Plugin name"
validations:
required: false
- type: textarea
id: description
attributes:
label: Description
description: Clearly describe the bug. What went wrong? What did you expect?
placeholder: |
I tried to use [feature], but instead of [expected behavior], it [actual behavior].
Error message (if any):
validations:
required: true
- type: textarea
id: steps
attributes:
label: Steps to Reproduce
description: How can we reproduce this issue?
placeholder: |
1. Click on...
2. Enter...
3. See error...
validations:
required: true
- type: input
id: openwebui-version
attributes:
label: OpenWebUI Version
description: What version of OpenWebUI are you using?
placeholder: "e.g., 0.3.0 or main"
validations:
required: true
- type: textarea
id: environment
attributes:
label: Operating System & Container
description: "Your operating system and deployment method"
placeholder: |
Example 1:
OS: macOS 14.3
Container: Docker (version 24.0.x)
Example 2:
OS: Ubuntu 22.04 LTS
Deployment: Docker Compose
validations:
required: true
- type: markdown
attributes:
value: |
## 📋 Debug Information (Optional)
To help diagnose the issue faster, please provide relevant logs:
**Frontend Console Logs** (Recommended):
1. Enable: Click avatar → Settings → General → Enable Plugin Debug Output
2. Open DevTools: Press `F12` (or `Cmd+Option+I` on Mac)
3. Go to Console tab and copy any error messages or `🛠️ Debug` output
**Server-Side Logs**:
- Docker: `docker logs <container-id>`
- Local: Check terminal output or log files
- type: textarea
id: logs
attributes:
label: Error Logs (Optional)
description: "Paste frontend console or server logs that show the error"
placeholder: |
Error message from console:
[Your logs here]
render: bash
validations:
required: false
- type: textarea
id: additional
attributes:
label: Additional Information (Optional)
description: "Screenshots, related issues, or other helpful details"
placeholder: |
- Screenshots (if applicable)
- Related issues or discussions
- Steps you've already tried to fix it
validations:
required: false
- type: checkboxes
id: checklist
attributes:
label: Checklist
options:
- label: I have searched for duplicate issues
required: true
- label: I have provided clear reproduction steps
required: true
- label: I have mentioned OpenWebUI version and OS/container info
required: true

11
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,11 @@
blank_issues_enabled: false
contact_links:
- name: Documentation
url: https://docs.openwebui.com/
about: Official OpenWebUI documentation
- name: Discussions
url: https://github.com/Fu-Jie/openwebui-extensions/discussions
about: Ask questions and discuss with the community
- name: OpenWebUI Repository
url: https://github.com/open-webui/open-webui
about: Main OpenWebUI project

View File

@@ -0,0 +1,104 @@
name: ✨ Feature Request
description: Suggest a new feature or improvement
title: "[FEATURE] "
labels: ["enhancement"]
assignees: []
body:
- type: markdown
attributes:
value: |
Thanks for your suggestion! Please describe the feature you'd like to see.
- type: dropdown
id: plugin-type
attributes:
label: Plugin Type (Optional)
description: Is this for a specific plugin type?
options:
- Action
- Filter
- Pipe
- Pipeline
- Tool
- Core/General
- Documentation
- Other
validations:
required: false
- type: dropdown
id: plugin-name
attributes:
label: Plugin Name (Optional)
description: Which plugin would benefit from this feature?
options:
- "Select a plugin..."
- "Action - Deep Dive"
- "Action - Export to Word Enhanced"
- "Action - Export to Excel"
- "Action - Flash Card"
- "Action - Smart Infographic"
- "Action - Smart Mind Map"
- "Filter - Async Context Compression"
- "Filter - Context & Model Enhancement Filter"
- "Filter - Folder Memory"
- "Filter - GitHub Copilot SDK Files Filter"
- "Filter - Markdown Normalizer"
- "Filter - Gemini Multimodel Filter"
- "Pipeline - MOE Prompt Refiner"
- "Pipe - GitHub Copilot SDK"
- "Pipe - iFlow SDK"
- "Tool - OpenWebUI Skills Manager"
- "Tool - Smart Infographic Tool"
- "Tool - Smart Mind Map Tool"
- "Core/General Improvement"
- "Other / Not Listed"
validations:
required: false
- type: textarea
id: description
attributes:
label: Feature Description
description: Clearly describe the feature you're requesting
placeholder: |
What feature would you like to see?
How would it work?
validations:
required: true
- type: textarea
id: motivation
attributes:
label: Motivation & Use Case
description: Why is this feature important? What problem does it solve?
placeholder: |
What's the pain point this solves?
How would it improve your workflow?
Who else would benefit from this?
validations:
required: true
- type: textarea
id: additional
attributes:
label: Additional Information (Optional)
description: "Mockups, alternatives considered, references, or examples"
placeholder: |
- Links to related plugins or tools
- Screenshots or mockups
- Alternative approaches you've considered
- Code examples or references
validations:
required: false
- type: checkboxes
id: checklist
attributes:
label: Checklist
options:
- label: I have searched existing issues/discussions for similar requests
required: true
- label: This feature would be useful for multiple users
required: false

View File

@@ -56,6 +56,11 @@ When bumping, update ALL 7+ files (code docstring + 2× README + 2× doc detail
- Never run `git commit`, `git push`, or create PRs automatically.
- After all edits, list what changed and why, then stop.
## Knowledge Capture (Mandatory)
Before ending the session, if you discovered any non-obvious internal API behaviour,
parameter injection quirk, or workaround, save it to `.agent/learnings/{topic}.md`.
Also browse `.agent/learnings/` at the start to reuse existing knowledge.
## Completion Output
- Modified files (full relative paths, one-line descriptions)
- Remaining manual checks

View File

@@ -22,6 +22,7 @@ You are the **planning specialist** for the `openwebui-extensions` repository.
- 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.
- Browse `.agent/learnings/` **first** to reuse existing knowledge before researching anything.
## Repository Plugin Inventory

View File

@@ -54,6 +54,9 @@ Full review rules are in .github/instructions/code-review.instructions.md.
- [ ] `docs/plugins/{type}/index.md` and `.zh.md` version badges updated.
- [ ] Root `README.md` / `README_CN.md` date badge updated.
**8. Knowledge Capture**
- [ ] Any non-obvious findings (API contracts, injection quirks, gotchas) documented in `.agent/learnings/{topic}.md`.
### 🟡 Non-blocking (suggestions)
- Copilot SDK tools: `params_type=MyParams` in `define_tool()`.
- Long tasks (>3s): periodic `_emit_notification("info")` every 5s.
@@ -68,4 +71,5 @@ Full review rules are in .github/instructions/code-review.instructions.md.
- **Blocking issues** (file:line references)
- **Non-blocking suggestions**
- **Pass / Fail verdict**
- **Knowledge captured?** (`.agent/learnings/` updated if any discoveries were made)
- **Next step**: Pass → handoff to Release Prep; Fail → return to Implementer with fix list

View File

@@ -32,6 +32,15 @@ plugins/actions/export_to_docx/
- `README.md` - English documentation
- `README_CN.md` - 中文文档
#### 文档交付与审阅 (Documentation Delivery for Review)
当任务涉及文档类内容时,例如 README、Guide、Post、Release Notes、Announcement、Development Docs
- **必须**同时提供英文版与中文版,方便审阅与校对。
- 若仓库最终只提交英文文件,也**必须**在对话中额外提供中文版草稿给维护者 review。
- 若用户未明确指定只保留单语文件,默认按双语交付处理。
- 中文版的目标是**便于审阅**,应忠实对应英文原意,可在表达上自然调整,但不得遗漏风险、限制、步骤或结论。
#### README 结构规范 (README Structure Standard)
所有插件 README 必须遵循以下统一结构顺序:
@@ -1151,6 +1160,7 @@ Filter 实例是**单例 (Singleton)**。
- [ ] **README 结构**:
- **Key Capabilities** (英文) / **核心功能** (中文): 必须包含所有核心功能
- **What's New** (英文) / **最新更新** (中文): 仅包含最新版本的变更信息
- [ ] **知识沉淀**: 开发过程中发现的非显而易见的规律、踩坑或内部 API 合约,必须记录到 `.agent/learnings/{topic}.md`
### 2. 🔄 一致性维护 (Consistency Maintenance)
@@ -1208,6 +1218,21 @@ Filter 实例是**单例 (Singleton)**。
使用 `@all-contributors please add @username for <type>` 指令。
### 6. 📖 知识沉淀 Knowledge Capture (Mandatory)
任何开发会话中发现的**非显而易见**的内部 API 行为、参数注入机制、Mock 对象要求或其他踩坑经验,
**必须**在会话结束前记录到 `.agent/learnings/{topic}.md`。
- **开始前**: 先浏览 `.agent/learnings/` 确认是否存在相关先验知识,避免重复调研。
- **格式规范**: 参见 `.agent/learnings/README.md`。
- **现有条目**: 见 `.agent/learnings/` 目录。
典型需要记录的内容:
- OpenWebUI 内部函数的参数注入机制
- Pipe 调用 Tool 时必须提供的上下文字段
- Mock Request 对象所需满足的接口契约
- 模型 ID 在不同上下文中的解析规则
---
## 📚 参考资源 (Reference Resources)

21
.github/gh-aw/README.md vendored Normal file
View File

@@ -0,0 +1,21 @@
# gh-aw Support Files
This directory stores repository-local support files for GitHub Agentic Workflows.
## Purpose
Keep review aids, policy notes, and human-facing mirrors out of `.github/workflows/` so only real gh-aw source workflows live there.
## Structure
- `review-mirrors/`: Chinese review mirrors and maintainer-facing explanations for workflow source files.
## Current Files
- `review-mirrors/aw-pr-maintainer-review.zh.md`: Chinese review mirror for `.github/workflows/aw-pr-maintainer-review.md`.
- `review-mirrors/aw-release-preflight.zh.md`: Chinese review mirror for `.github/workflows/aw-release-preflight.md`.
- `review-mirrors/aw-ci-audit.zh.md`: Chinese review mirror for `.github/workflows/aw-ci-audit.md`.
## Rule
Files in this directory are for maintainer review and documentation only. They are not gh-aw workflow source files and should not be compiled.

View File

@@ -0,0 +1,249 @@
# aw-ci-audit 中文对照
对应源文件:`.github/workflows/aw-ci-audit.md`
用途:这是一份给维护者 review 用的中文对照说明,不是 gh-aw 工作流源文件,也不参与 `gh aw compile`
## 工作流定位
这个工作流的目标是做“CI / 自动化健康审计”。
它不是日志转储器,也不是自动修复器,而是用于:
- 检查近期仓库自动化是否出现可重复的失败模式
- 分析 release、publish、stats 等关键工作流的薄弱点
- 只在有新且可操作的诊断结论时,创建一条维护 issue
如果没有新的可操作诊断,或者问题已经被现有 issue 覆盖,就执行 `noop`
## Frontmatter 对照
### 触发方式
- `schedule: daily`
- `workflow_dispatch`
- `roles: all`
- `skip-bots`
- `github-actions`
- `copilot`
- `dependabot`
- `renovate`
说明:这套设计更适合“定期体检 + 手动补查”,而不是直接绑到不确定的 workflow failure 事件上。
### 权限
当前设计为只读:
- `contents: read`
- `issues: read`
- `pull-requests: read`
- `actions: read`
说明:工作流只做诊断分析,不改代码、不发 release、不创建 PR。
### Safe Outputs
已配置:
- `create-issue`
- 标题前缀:`[ci-audit] `
- labels`ci-audit``maintenance`
- 不自动关闭旧 issue
最终只能二选一:
- 有新且可操作的诊断时执行 `create_issue`
- 无新问题时执行 `noop`
### 工具
- `github`
- `repos`
- `issues`
- `pull_requests`
- `bash`
- 仅开放只读类命令,如 `pwd``ls``cat``rg``git diff``git show`
## 正文指令对照
## 主要目标
要求代理审计:
- release 相关 workflow 的失败或波动
- 插件发布失败
- 社区统计更新回归
- 重复出现的 workflow 脆弱点
- 维护者真正可以执行的下一步动作
明确限制:
- 只做诊断
- 不改文件
- 不推代码
- 不开 PR
- 不发 release
## 高优先级依据文件
在形成结论前,优先把这些文件当成“自动化规则源”:
- `.github/copilot-instructions.md`
- `.github/workflows/release.yml`
- `.github/workflows/publish_plugin.yml`
- `.github/workflows/publish_new_plugin.yml`
- `.github/workflows/plugin-version-check.yml`
- `.github/workflows/community-stats.yml`
- `docs/development/gh-aw-integration-plan.md`
- `docs/development/gh-aw-integration-plan.zh.md`
## 重点关注的目标工作流
优先检查:
- `release.yml`
- `publish_plugin.yml`
- `publish_new_plugin.yml`
- `plugin-version-check.yml`
- `community-stats.yml`
- `deploy.yml`
如果这些没有明显问题,不要无限扩大范围。
## 审查范围
聚焦“近期失败或可疑自动化信号”,并优先给出基于本仓库结构的诊断,而不是泛泛的 CI 建议。
它应该像“在看仓库自动化健康趋势的维护者”,而不是普通日志摘要机器人。
## 重点检查项
### 1. Release 与 Publish 失败
检查近期失败是否指向这些可操作问题:
- 版本提取或比较逻辑漂移
- release note 打包缺口
- publish 脚本的认证或环境问题
- workflow 中的结构假设已经不匹配当前仓库
- 如果不改仓库逻辑,就可能持续复现的失败
### 2. Stats 与定时任务稳定性
检查定时维护任务是否出现这些脆弱点:
- community stats 该提交时不再提交
- badge / docs 生成逻辑过时
- 依赖外部 API 的任务反复因同类原因失败
- schedule 驱动任务制造低价值噪音
### 3. 维护者信号质量
只有当结论“真的值得维护者处理”时,才创建 issue。
适合开 issue 的情况:
- 同类失败在多次运行中重复出现
- workflow 逻辑与当前仓库结构不匹配
- 大概率缺 secret / 权限 / 路径假设过时
- 重复出现的低信号失败值得过滤或加固
不要为一次性噪音失败开 issue除非它很可能复发。
### 4. 已有 Issue 感知
在创建新 issue 前,先判断是否已有 open issue 覆盖同一类 CI 问题。
如果已有 issue 已经足够覆盖,就优先 `noop`,避免制造重复单。
## 严重级别
只允许三档:
- `High`
- 高概率重复发生,且会持续影响仓库自动化
- `Medium`
- 建议尽快修,以降低维护成本或 workflow 漂移
- `Low`
- 可选的稳健性增强或清理建议
并且明确要求:
- 不要为了开 issue 而硬造问题
## Issue 格式
如果要创建 issue必须只有一条维护 issue。
要求:
- 英文
- 简洁
- 先写 findings不写空泛表扬
- 带可点击路径引用
- 不用嵌套列表
- 不要粘贴大段原始日志,除非短摘录确实必要
固定结构:
```markdown
## CI Audit
### Summary
Short diagnosis of the failure pattern or automation risk.
### Findings
- `path/to/file`: specific problem or likely root cause
### Suggested Next Steps
- concrete maintainer action
- concrete maintainer action
### Notes
- Mention whether this appears recurring, new, or already partially mitigated.
```
补充规则:
- 正常情况下控制在约 300 词以内
- 如果是相关联的问题,合并成一个 issue不要拆多个
- 优先提交“单个可执行诊断”,而不是大杂烩
## No-Issue 规则
如果没有值得报告的新诊断:
- 不要创建状态汇报型 issue
- 不要复述 workflows 看起来健康
- 直接走 `noop`
示例:
```json
{"noop": {"message": "No action needed: reviewed recent repository automation signals and found no new actionable CI diagnosis worth opening as a maintenance issue."}}
```
## 建议执行流程
1. 检查近期仓库自动化上下文
2. 优先检查目标工作流
3. 识别可重复或仓库特定的失败模式
4. 判断该问题是否已被 open issue 覆盖
5. 只有在诊断“新且可操作”时,才起草最短有用的维护 issue
6. 最终只执行一次 `create_issue` 或一次 `noop`
## 额外约束
- 不要为单次低信号瞬时失败开 issue
- 除非失败模式非常明确,否则不要顺势要求大规模重构
- 优先给出仓库特定原因,而不是泛泛的“重试试试”
- 如果根因不确定,要把不确定性写明
- 如果现有 issue 已经覆盖,优先 `noop` 而不是重复开单
## 最终要求
必须以且仅以一次 safe output 结束:
- 有新且可操作的诊断:`create_issue`
- 无新问题:`noop`

View File

@@ -0,0 +1,268 @@
# aw-pr-maintainer-review 中文对照
对应源文件:`.github/workflows/aw-pr-maintainer-review.md`
用途:这是一份给维护者 review 用的中文对照说明,不是 gh-aw 工作流源文件,也不参与 `gh aw compile`
## 工作流定位
这个工作流的目标是对触发 PR 做一次“维护者语义审查”。
它不是通用 code review 机器人,也不是自动修复器,而是用来检查以下问题:
- 是否违反本仓库插件开发规范
- 是否缺失应同步更新的 README / README_CN / docs 镜像文件
- 是否存在发布准备层面的遗漏
- 是否引入明显的高风险行为回归
如果 PR 已经足够合规,没有可操作的维护者反馈,就不评论,而是执行 `noop`
## Frontmatter 对照
### 触发方式
- `pull_request`
- 类型:`opened``reopened``synchronize``ready_for_review`
- 路径限制:
- `plugins/**`
- `docs/**`
- `.github/**`
- `README.md`
- `README_CN.md`
- `workflow_dispatch`
- `roles: all`
- `skip-bots`
- `github-actions`
- `copilot`
- `dependabot`
- `renovate`
### 权限
当前设计为只读:
- `contents: read`
- `issues: read`
- `pull-requests: read`
说明:工作流不会直接改代码,也不会提交 review comment 之外的写操作。
### Safe Outputs
已配置:
- `add-comment`
- 目标:当前触发 PR
- 最多 1 条
- 隐藏旧评论
- 不加 footer
同时要求最终必须二选一:
- 有问题时执行 `add_comment`
- 无问题时执行 `noop`
### 工具
- `github`
- `repos`
- `issues`
- `pull_requests`
- `bash`
- 仅开放只读类命令,如 `pwd``ls``cat``rg``git diff``git show`
## 正文指令对照
## 主要目标
要求代理审查:
- 仓库标准合规性
- 缺失的同步更新文件
- 发布准备缺口
- 文档漂移
- 插件代码中的高风险回归
明确限制:
- 只做 review
- 不改文件
- 不推代码
- 不创建 PR
## 高优先级依据文件
在形成结论前,优先把这些文件当成“本仓库规则源”:
- `.github/copilot-instructions.md`
- `.github/instructions/code-review.instructions.md`
- `.github/instructions/commit-message.instructions.md`
- `.github/skills/release-prep/SKILL.md`
- `.github/skills/doc-mirror-sync/SKILL.md`
- `docs/development/gh-aw-integration-plan.md`
- `docs/development/gh-aw-integration-plan.zh.md`
## 审查范围
- 先看 PR diff 和 changed files
- 只有在验证一致性时,才扩展读取关联文件
- 优先遵循“仓库特定规则”,而不是泛泛的最佳实践
换句话说,它应该像“熟悉本仓库的维护者”,而不是通用 lint bot。
## 重点检查项
### 1. 插件代码规范
`plugins/**/*.py` 变化时,重点看:
- 是否保持单文件 i18n 模式
- 用户可见文本是否进入翻译字典
- 是否使用 `_get_user_context``_get_chat_context`
- `__event_call__` 的 JS 执行是否具备 timeout 防护和前端兜底
- 是否引入 `print()` 到生产插件代码
- emitter 是否安全判空
- filter 插件是否把请求级可变状态塞到 `self`
- Copilot SDK / OpenWebUI tool 定义是否仍符合仓库规范
### 2. 版本与发布卫生
`plugins/**/*.py` 改动时,检查是否“应该同步但没同步”:
- 插件 docstring 的 `version:`
- 插件目录下 `README.md`
- 插件目录下 `README_CN.md`
- `docs/plugins/**` 下的镜像页面
- `docs/plugins/{type}/index.md` 等索引文件
- 如果是明显 release-prep 类型 PR再看根 `README.md``README_CN.md` 日期 badge
这里的关键语义是:
- 不是每个 PR 都必须当发布处理
- 只有在“用户可见行为、元数据、版本化文档、发布面内容”发生变化时,才提示缺失同步
### 3. 文档同步
当插件 README 改动时,检查是否应同步 docs 镜像:
- `plugins/actions/{name}/README.md` -> `docs/plugins/actions/{name}.md`
- `plugins/actions/{name}/README_CN.md` -> `docs/plugins/actions/{name}.zh.md`
- `plugins/filters/{name}/README.md` -> `docs/plugins/filters/{name}.md`
- `plugins/filters/{name}/README_CN.md` -> `docs/plugins/filters/{name}.zh.md`
- `plugins/pipes/{name}/README.md` -> `docs/plugins/pipes/{name}.md`
- `plugins/pipes/{name}/README_CN.md` -> `docs/plugins/pipes/{name}.zh.md`
- `plugins/pipelines/{name}/README.md` -> `docs/plugins/pipelines/{name}.md`
- `plugins/pipelines/{name}/README_CN.md` -> `docs/plugins/pipelines/{name}.zh.md`
- `plugins/tools/{name}/README.md` -> `docs/plugins/tools/{name}.md`
- `plugins/tools/{name}/README_CN.md` -> `docs/plugins/tools/{name}.zh.md`
如果是 docs-only 且明显有意为之,不要过度报错。
### 4. PR 质量
只在“确实让维护者审查变难”时,才指出 PR 描述缺失这些内容:
- 改了什么
- 为什么改
- 是否需要迁移或重新配置
## 严重级别
只允许三档:
- `Blocking`
- 大概率 bug、发布回归、缺少必需同步、严重规范破坏
- `Important`
- 应该合并前修,但不一定是直接运行时错误
- `Minor`
- 建议项,可选
并且明确要求:
- 不要为了留言而硬凑问题
## 评论格式
如果要评论,必须只有一条总结评论。
要求:
- 英文
- 简洁
- 先给 findings不先夸赞
- 带可点击路径引用
- 不使用嵌套列表
- 不要机械复述 diff
固定结构:
```markdown
## PR Maintainer Review
### Blocking
- `path/to/file`: specific issue and why it matters
### Important
- `path/to/file`: specific issue and what sync/check is missing
### Minor
- `path/to/file`: optional improvement or consistency note
### Merge Readiness
- Ready after the items above are addressed.
```
补充规则:
- 空 section 要省略
- 如果只有一个严重级别,只保留那个 section 和 `Merge Readiness`
- 正常情况下控制在约 250 词以内
## No-Comment 规则
如果没有有意义的维护者反馈:
- 不要发“看起来不错”这类表扬评论
- 不要复述 checks passed
- 直接走 `noop`
示例:
```json
{"noop": {"message": "No action needed: reviewed the PR diff and repository sync expectations, and found no actionable maintainer feedback."}}
```
## 建议执行流程
1. 找出变更文件
2. 读取高优先级规则文件
3. 对照插件审查规范检查插件代码
4. 对照 doc mirror 规则检查 README / docs
5. 判断是否缺失 version sync 或 release-facing 文件
6. 先起草最短但有用的维护者总结
7. 最终只执行一次 `add_comment` 或一次 `noop`
## 额外约束
- 不要要求与本 PR 无关的大重构
- 小型内部变更不要强拉成 release-prep
- 明显是私有/内部改动时,不要强制要求 docs sync
- 优先给出“仓库特定”的反馈,而不是通用 code review 废话
- 如果你不确定某个同步文件是否必需,把级别降为 `Important`
- 如果问题依赖 PR 意图但当前信息不足,要把表述写成“条件性判断”,不要装作确定
## 最终要求
必须以且仅以一次 safe output 结束:
- 有可操作反馈:`add_comment`
- 无可操作反馈:`noop`
## Review 结论
这份英文源工作流目前已经可以作为后续 `gh aw compile` 的候选源文件。
中文镜像的目的只有两个:
- 方便你逐段审阅策略是否符合预期
- 避免把中文说明混进真正要编译的 workflow 源文件

View File

@@ -0,0 +1,275 @@
# aw-release-preflight 中文对照
对应源文件:`.github/workflows/aw-release-preflight.md`
用途:这是一份给维护者 review 用的中文对照说明,不是 gh-aw 工作流源文件,也不参与 `gh aw compile`
## 工作流定位
这个工作流的目标是对触发变更做一次“发布前预检语义审查”。
它不是发布执行器,也不是自动补版本工具,而是用于判断:
- 这次改动是否真的在做 release-prep
- 如果是在做 release-prep版本同步是否完整
- 双语 README、docs 镜像、release notes 是否齐全
- 是否存在会影响发布质量的说明缺失或文档漂移
如果当前变更并不是发布准备,或者已经足够一致、没有可操作反馈,就执行 `noop`
## Frontmatter 对照
### 触发方式
- `pull_request`
- 类型:`opened``reopened``synchronize``ready_for_review`
- 路径限制:
- `plugins/**/*.py`
- `plugins/**/README.md`
- `plugins/**/README_CN.md`
- `plugins/**/v*.md`
- `plugins/**/v*_CN.md`
- `docs/plugins/**/*.md`
- `README.md`
- `README_CN.md`
- `.github/**`
- `workflow_dispatch`
- `roles: all`
- `skip-bots`
- `github-actions`
- `copilot`
- `dependabot`
- `renovate`
### 权限
当前设计为只读:
- `contents: read`
- `issues: read`
- `pull-requests: read`
说明:工作流不会发 release、不会推代码、不会改文件。
### Safe Outputs
已配置:
- `add-comment`
- 目标:当前触发 PR
- 最多 1 条
- 隐藏旧评论
- 不加 footer
最终只能二选一:
- 有问题时执行 `add_comment`
- 无问题时执行 `noop`
### 工具
- `github`
- `repos`
- `issues`
- `pull_requests`
- `bash`
- 仅开放只读类命令,如 `pwd``ls``cat``rg``git diff``git show`
## 正文指令对照
## 主要目标
要求代理检查:
- 版本同步完整性
- 双语 README 与 docs 一致性
- release notes 完整性
- 发布面索引或 badge 漂移
- 用户可见发布是否缺失迁移说明或维护者上下文
明确限制:
- 只做 review
- 不改文件
- 不推代码
- 不创建 release
- 不创建 PR
## 高优先级依据文件
在形成结论前,优先把这些文件当成“发布规则源”:
- `.github/copilot-instructions.md`
- `.github/instructions/commit-message.instructions.md`
- `.github/skills/release-prep/SKILL.md`
- `.github/skills/doc-mirror-sync/SKILL.md`
- `.github/workflows/release.yml`
- `docs/development/gh-aw-integration-plan.md`
- `docs/development/gh-aw-integration-plan.zh.md`
## 审查范围
- 从 PR diff 和 changed files 开始
- 只有在验证发布同步时才扩展到相关 release-facing 文件
- 优先遵循仓库既有 release-prep 规则,而不是泛泛的 release 建议
换句话说,它应该像“合并前最后做一致性复核的维护者”。
## 重点检查项
### 1. 发布相关文件中的版本同步
当某个插件明显在准备发版时,检查这些位置是否同步:
- 插件 Python docstring 的 `version:`
- 插件目录下 `README.md`
- 插件目录下 `README_CN.md`
- `docs/plugins/**` 英文镜像页
- `docs/plugins/**/*.zh.md` 中文镜像页
- `docs/plugins/{type}/index.md` 中该插件的条目或版本 badge
- `docs/plugins/{type}/index.zh.md` 中该插件的条目或版本 badge
但只有在“这次改动明显带有发布意图”时才提示,不要把所有 PR 都按发布处理。
### 2. README 与 docs 镜像一致性
当插件 README 变化时,检查 docs 镜像是否同步。
路径映射:
- `plugins/actions/{name}/README.md` -> `docs/plugins/actions/{name}.md`
- `plugins/actions/{name}/README_CN.md` -> `docs/plugins/actions/{name}.zh.md`
- `plugins/filters/{name}/README.md` -> `docs/plugins/filters/{name}.md`
- `plugins/filters/{name}/README_CN.md` -> `docs/plugins/filters/{name}.zh.md`
- `plugins/pipes/{name}/README.md` -> `docs/plugins/pipes/{name}.md`
- `plugins/pipes/{name}/README_CN.md` -> `docs/plugins/pipes/{name}.zh.md`
- `plugins/pipelines/{name}/README.md` -> `docs/plugins/pipelines/{name}.md`
- `plugins/pipelines/{name}/README_CN.md` -> `docs/plugins/pipelines/{name}.zh.md`
- `plugins/tools/{name}/README.md` -> `docs/plugins/tools/{name}.md`
- `plugins/tools/{name}/README_CN.md` -> `docs/plugins/tools/{name}.zh.md`
如果是纯文档调整、而且并非发版预备,不要过度报错。
### 3. What's New 与 Release Notes 覆盖度
当这次更新明显是发布面插件更新时,检查:
- `What's New` 是否只反映最新版本
- `最新更新` 是否与英文对应
- 是否存在 `v{version}.md``v{version}_CN.md`
- release notes 是否覆盖当前 diff 中有意义的功能、修复、文档或迁移变化
对纯内部小改动,不要强制要求 release notes。
### 4. 根 README 与发布面索引漂移
当改动明显面向正式发布时,再检查:
-`README.md` 的日期 badge
-`README_CN.md` 的日期 badge
- `docs/plugins/**/index.md`
- `docs/plugins/**/index.zh.md`
不要把这种检查强加给普通内部 PR。
### 5. 维护者上下文与发布清晰度
检查 PR 描述或发布面文案是否缺少关键上下文:
- 这次到底发布了什么
- 为什么这次发布值得做
- 是否需要迁移或重新配置
只有在缺失信息会明显增加 release review 成本时,才提示。
## 严重级别
只允许三档:
- `Blocking`
- 高概率发布回归、缺少必要版本同步、发布面更新明显不完整
- `Important`
- 合并前最好修,避免发布混乱或文档漂移
- `Minor`
- 可选的发布面清理或一致性建议
并且明确要求:
- 不要为了留言而造问题
## 评论格式
如果要评论,必须只有一条总结评论。
要求:
- 英文
- 简洁
- 先给 findings不先夸赞
- 带可点击路径引用
- 不使用嵌套列表
- 不要机械复述 diff
固定结构:
```markdown
## Release Preflight Review
### Blocking
- `path/to/file`: specific release-facing problem and why it matters
### Important
- `path/to/file`: missing sync or release-documentation gap
### Minor
- `path/to/file`: optional cleanup or consistency improvement
### Release Readiness
- Ready after the items above are addressed.
```
补充规则:
- 空 section 要省略
- 如果只有一个严重级别,只保留那个 section 和 `Release Readiness`
- 正常情况下控制在约 250 词以内
## No-Comment 规则
如果没有有意义的发布前预检反馈:
- 不要发“看起来不错”这类表扬评论
- 不要复述 checks passed
- 直接走 `noop`
示例:
```json
{"noop": {"message": "No action needed: reviewed the release-facing diff, version-sync expectations, and bilingual documentation coverage, and found no actionable preflight feedback."}}
```
## 建议执行流程
1. 判断这次改动是否真的带有发布意图
2. 检查 PR diff 中的变更文件
3. 读取仓库的 release-prep 规则文件
4. 只有在存在发布意图时,才检查 plugin version sync
5. 检查 README、README_CN、docs 镜像、索引和 release notes 是否漂移
6. 起草最短但有用的维护者总结
7. 最终只执行一次 `add_comment` 或一次 `noop`
## 额外约束
- 不要把完整 release-prep 要求硬套到微小内部改动上
- 非明确发布型 PR不要强制要求根 README 日期 badge 更新
- 如果这次改动并不现实地构成发版预备,就不要强求 release notes
- 优先给出仓库特定的同步反馈,而不是泛泛的发布建议
- 如果不确定某个 release-facing 同步文件是否必需,把级别降为 `Important`
- 如果问题依赖“推测出来的意图”,要用条件式表述,不要装作确定
## 最终要求
必须以且仅以一次 safe output 结束:
- 有可操作反馈:`add_comment`
- 无可操作反馈:`noop`

View File

@@ -0,0 +1,150 @@
---
name: publish-no-version-bump
description: Commit and push code to GitHub, then publish to OpenWebUI official marketplace without updating version. Use when fixing bugs or optimizing performance that doesn't warrant a version bump.
---
# Publish Without Version Bump
## Overview
This skill handles the workflow for pushing code changes to the remote repository and syncing them to the OpenWebUI official marketplace **without incrementing the plugin version number**.
This is useful for:
- Bug fixes and patches
- Performance optimizations
- Code refactoring
- Documentation fixes
- Linting and code quality improvements
## When to Use
Use this skill when:
- You've made non-breaking changes (bug fixes, optimizations, refactoring)
- The functionality hasn't changed significantly
- The user-facing behavior is unchanged or only improved
- There's no need to bump the semantic version
**Do NOT use** if:
- You're adding new features → use `release-prep` instead
- You're making breaking changes → use `release-prep` instead
- The version should be incremented → use `version-bumper` first
## Workflow
### Step 1 — Stage and Commit Changes
Ensure all desired code changes are staged in git:
```bash
git status # Verify what will be committed
git add -A # Stage all changes
```
Create a descriptive commit message using Conventional Commits format:
```
fix(plugin-name): brief description
- Detailed change 1
- Detailed change 2
```
Example commit types:
- `fix:` — Bug fixes, patches
- `perf:` — Performance improvements, optimization
- `refactor:` — Code restructuring without behavior change
- `test:` — Test updates
- `docs:` — Documentation changes
**Key Rule**: The commit message should make clear that this is NOT a new feature release (no `feat:` type).
### Step 2 — Push to Remote
Push the commit to the main branch:
```bash
git commit -m "<message>" && git push
```
Verify the push succeeded by checking GitHub.
### Step 3 — Publish to Official Marketplace
Run the publish script with `--force` flag to update the marketplace without version change:
```bash
python scripts/publish_plugin.py --force
```
**Important**: The `--force` flag ensures the marketplace version is updated even if the version string in the plugin file hasn't changed.
### Step 4 — Verify Publication
Check that the plugin was successfully updated in the official marketplace:
1. Visit https://openwebui.com/f/
2. Search for your plugin name
3. Verify the code is up-to-date
4. Confirm the version number **has NOT changed**
---
## Command Reference
### Full Workflow (Manual)
```bash
# 1. Stage and commit
git add -A
git commit -m "fix(copilot-sdk): description here"
# 2. Push
git push
# 3. Publish to marketplace
python scripts/publish_plugin.py --force
# 4. Verify
# Check OpenWebUI marketplace for the updated code
```
### Automated (Using This Skill)
When you invoke this skill with a plugin path, Copilot will:
1. Verify staged changes and create the commit
2. Push to the remote repository
3. Execute the publish script
4. Report success/failure status
---
## Implementation Notes
### Version Handling
- The plugin's version string in `docstring` (line ~10) remains **unchanged**
- The `openwebui_id` in the plugin file must be present for the publish script to work
- If the plugin hasn't been published before, use `publish_plugin.py --new <dir>` instead
### Dry Run
To preview what would be published without actually updating the marketplace:
```bash
python scripts/publish_plugin.py --force --dry-run
```
### Troubleshooting
| Issue | Solution |
|-------|----------|
| `Error: openwebui_id not found` | The plugin hasn't been published yet. Use `publish_plugin.py --new <dir>` for first-time publishing. |
| `Failed to authenticate` | Check that the `OPENWEBUI_API_KEY` environment variable is set. |
| `Skipped (version unchanged)` | This is normal. Without `--force`, unchanged versions are skipped. We use `--force` to override this. |
---
## Related Skills
- **`release-prep`** — Use when you need to bump the version and create release notes
- **`version-bumper`** — Use to manually update version across all 7+ files
- **`pr-submitter`** — Use to create a PR instead of pushing directly to main

View File

@@ -17,6 +17,7 @@ When assisting with the development of `openwebui-extensions`, you (Antigravity)
- **Open WebUI**: `../open-webui/` (Core platform context)
- **Skills**: `../skills/` (Reusable expertise library)
- **Awesome Copilot**: `../awesome-copilot/` (Shared extensions & resources)
- **Open Terminal**: `../open-terminal/` (Terminal integration service)
### Plugin-Specific (Relevant to GitHub Copilot SDK)

222
.github/workflows/aw-ci-audit.md vendored Normal file
View File

@@ -0,0 +1,222 @@
---
description: "CI audit workflow for failed releases, publish jobs, stats updates, and other important repository automation"
private: true
labels: [automation, diagnostics, ci, gh-aw]
metadata:
author: Fu-Jie
category: maintenance
maturity: draft
on:
schedule: daily
workflow_dispatch:
roles: all
skip-bots: [github-actions, copilot, dependabot, renovate]
permissions:
contents: read
issues: read
pull-requests: read
actions: read
engine: copilot
network:
allowed:
- defaults
safe-outputs:
create-issue:
title-prefix: "[ci-audit] "
labels: [ci-audit, maintenance]
close-older-issues: false
allowed-github-references: [repo]
timeout-minutes: 15
tools:
github:
toolsets: [repos, issues, pull_requests]
bash:
- pwd
- ls
- cat
- head
- tail
- grep
- wc
- rg
- git status
- git diff
- git show
- git ls-files
---
# CI Audit
You are the repository maintainer assistant for `Fu-Jie/openwebui-extensions`.
Your job is to inspect recent repository automation health and create **one concise maintenance issue only when there is actionable CI or automation feedback**.
If there is no meaningful failure pattern, no new actionable diagnosis, or no useful maintainer issue to open, you **must call `noop`** with a short explanation.
## Primary Goal
Audit recent automation health for:
- failed or flaky release-related workflows
- plugin publishing failures
- community stats update regressions
- repeated workflow drift or fragile maintenance steps
- repository-specific next steps maintainers can actually act on
This workflow is **diagnostic-only**. Do not modify files, push code, open pull requests, or create releases.
## High-Priority Source Files
Use these files as the authoritative context before forming conclusions:
- `.github/copilot-instructions.md`
- `.github/workflows/release.yml`
- `.github/workflows/publish_plugin.yml`
- `.github/workflows/publish_new_plugin.yml`
- `.github/workflows/plugin-version-check.yml`
- `.github/workflows/community-stats.yml`
- `docs/development/gh-aw-integration-plan.md`
- `docs/development/gh-aw-integration-plan.zh.md`
## Target Workflows
Prioritize these workflows first:
- `release.yml`
- `publish_plugin.yml`
- `publish_new_plugin.yml`
- `plugin-version-check.yml`
- `community-stats.yml`
- `deploy.yml`
If there are no meaningful issues there, do not widen scope unnecessarily.
## Review Scope
Focus on recent failed or suspicious automation runs and repository-facing symptoms. Prefer diagnosis that is grounded in repository context, not generic CI advice.
This workflow should behave like a maintainer who is reviewing workflow health trends, not like a generic log summarizer.
Focus especially on these areas:
### 1. Release and Publish Failures
Inspect whether recent failures suggest actionable problems such as:
- version extraction or comparison drift
- release-note packaging gaps
- publish-script authentication or environment issues
- assumptions in release jobs that no longer match repository structure
- failures that are likely to recur until repository logic changes
### 2. Stats and Scheduled Workflow Reliability
Inspect whether scheduled maintenance jobs show drift or fragility such as:
- community stats commits no longer happening when expected
- badge or docs generation assumptions becoming stale
- external API dependent jobs failing in repeatable ways
- schedule-driven jobs causing noisy or low-value churn
### 3. Signal Quality for Maintainers
Only create an issue if there is a useful diagnosis with at least one concrete next step.
Good issue-worthy findings include:
- a repeated failure signature across runs
- a repository mismatch between workflow logic and current file layout
- a likely missing secret, missing permission, or stale path assumption
- repeated low-signal failures that should be filtered or hardened
Do not open issues for one-off noise unless the failure pattern is likely to recur.
### 4. Existing Issue Awareness
Before creating a new issue, check whether a recent open issue already appears to cover the same CI failure pattern.
If an existing issue already covers the problem well enough, prefer `noop` and mention that the diagnosis is already tracked.
## Severity Model
Use three levels only:
- `High`: likely recurring CI or automation failure with repository impact
- `Medium`: useful to fix soon to reduce maintenance burden or workflow drift
- `Low`: optional hardening or cleanup suggestion
Do not invent issues just to create a report.
## Issue Creation Rules
Create **one maintenance issue** only if there is actionable new diagnosis.
The issue must:
- be in English
- be concise and maintainer-like
- lead with findings, not generic praise
- include clickable file references like ``.github/workflows/release.yml`` or ``scripts/publish_plugin.py``
- avoid nested bullets
- avoid pasting raw logs unless a short excerpt is critical
Use this exact structure when creating the issue:
```markdown
## CI Audit
### Summary
Short diagnosis of the failure pattern or automation risk.
### Findings
- `path/to/file`: specific problem or likely root cause
### Suggested Next Steps
- concrete maintainer action
- concrete maintainer action
### Notes
- Mention whether this appears recurring, new, or already partially mitigated.
```
Rules:
- Keep the issue under about 300 words unless multiple workflows are affected.
- If there are multiple related findings, group them into one issue rather than opening separate issues.
- Prefer a single, actionable diagnosis over a broad laundry list.
## No-Issue Rule
If there is no meaningful new diagnosis to report:
- do not create a status-only issue
- do not restate that workflows look healthy
- call `noop` with a short explanation like:
```json
{"noop": {"message": "No action needed: reviewed recent repository automation signals and found no new actionable CI diagnosis worth opening as a maintenance issue."}}
```
## Suggested Audit Process
1. Inspect recent repository automation context.
2. Prioritize the target workflows listed above.
3. Identify recurring or repository-specific failure patterns.
4. Check whether the problem is already tracked in an open issue.
5. Draft the shortest useful maintenance issue only if the diagnosis is actionable and new.
6. Finish with exactly one `create_issue` or one `noop`.
## Important Constraints
- Do not create an issue for a single low-signal transient failure.
- Do not propose large refactors unless the failure pattern clearly justifies them.
- Prefer repository-specific causes over generic "retry later" style advice.
- If the likely root cause is uncertain, state the uncertainty explicitly.
- If the pattern appears already tracked, prefer `noop` over duplicate issue creation.
## Final Requirement
You **must** finish with exactly one safe output action:
- `create_issue` if there is actionable new diagnosis
- `noop` if there is not

View File

@@ -0,0 +1,236 @@
---
description: "Semantic PR maintainer review for plugin standards, bilingual docs sync, and release readiness gaps"
private: true
labels: [automation, review, pull-request, gh-aw]
metadata:
author: Fu-Jie
category: maintenance
maturity: draft
on:
pull_request:
types: [opened, reopened, synchronize, ready_for_review]
paths:
- 'plugins/**'
- 'docs/**'
- '.github/**'
- 'README.md'
- 'README_CN.md'
forks: ["*"]
workflow_dispatch:
roles: all
skip-bots: [github-actions, copilot, dependabot, renovate]
permissions:
contents: read
issues: read
pull-requests: read
engine: copilot
network:
allowed:
- defaults
safe-outputs:
add-comment:
target: triggering
max: 1
hide-older-comments: true
footer: false
allowed-github-references: [repo]
timeout-minutes: 12
tools:
github:
toolsets: [repos, issues, pull_requests]
bash:
- pwd
- ls
- cat
- head
- tail
- grep
- wc
- rg
- git status
- git diff
- git show
- git ls-files
---
# PR Maintainer Review
You are the repository maintainer assistant for `Fu-Jie/openwebui-extensions`.
Your job is to review the triggering pull request against this repository's standards and leave **one concise summary comment only when there is actionable feedback**.
If the PR already looks compliant enough and there is no useful maintainer feedback to add, you **must call `noop`** with a short explanation.
## Primary Goal
Review the PR for:
- repository-standard compliance
- missing synchronized file updates
- release-readiness gaps
- documentation drift introduced by the change
- risky behavior regressions in plugin code
This workflow is **review-only**. Do not attempt to modify files, push code, or open pull requests.
## High-Priority Source Files
Use these files as the authoritative rule set before forming conclusions:
- `.github/copilot-instructions.md`
- `.github/instructions/code-review.instructions.md`
- `.github/instructions/commit-message.instructions.md`
- `.github/skills/release-prep/SKILL.md`
- `.github/skills/doc-mirror-sync/SKILL.md`
- `docs/development/gh-aw-integration-plan.md`
- `docs/development/gh-aw-integration-plan.zh.md`
## Review Scope
Start from the PR diff and changed files only. Expand into related files only when necessary to verify consistency.
Prioritize repository policy over generic best practices. This workflow should behave like a maintainer who knows this repository well, not like a broad lint bot.
Focus especially on these areas:
### 1. Plugin Code Standards
When a plugin Python file changes, check for repository-specific correctness:
- single-file i18n pattern is preserved
- user-visible text is routed through translations where appropriate
- `_get_user_context` and `_get_chat_context` are used instead of fragile direct access
- `__event_call__` JavaScript execution has timeout guards and JS-side fallback handling
- `print()` is not introduced in production plugin code
- emitter usage is guarded safely
- filter plugins do not store request-scoped mutable state on `self`
- OpenWebUI/Copilot SDK tool definitions remain consistent with repository conventions
### 2. Versioning and Release Hygiene
When `plugins/**/*.py` changes, verify whether the PR also updates what should normally move with it:
- plugin docstring `version:` changed when behavior changed
- local `README.md` and `README_CN.md` changed where user-visible behavior changed
- mirrored docs under `docs/plugins/**` changed where required
- docs plugin indexes changed if a published version badge or listing text should change
- root `README.md` and `README_CN.md` updated date badge if this PR is clearly release-prep oriented
Do not require every PR to be full release prep. Only flag missing sync files when the PR clearly changes published behavior, plugin metadata, versioned documentation, or release-facing content.
### 3. Documentation Sync
When plugin READMEs change, check whether matching docs mirrors should also change:
- `plugins/{type}/{name}/README.md` -> `docs/plugins/{type}/{name}.md`
- `plugins/{type}/{name}/README_CN.md` -> `docs/plugins/{type}/{name}.zh.md`
When docs-only changes are intentional, avoid over-reporting.
Useful path mappings:
- `plugins/actions/{name}/README.md` -> `docs/plugins/actions/{name}.md`
- `plugins/actions/{name}/README_CN.md` -> `docs/plugins/actions/{name}.zh.md`
- `plugins/filters/{name}/README.md` -> `docs/plugins/filters/{name}.md`
- `plugins/filters/{name}/README_CN.md` -> `docs/plugins/filters/{name}.zh.md`
- `plugins/pipes/{name}/README.md` -> `docs/plugins/pipes/{name}.md`
- `plugins/pipes/{name}/README_CN.md` -> `docs/plugins/pipes/{name}.zh.md`
- `plugins/pipelines/{name}/README.md` -> `docs/plugins/pipelines/{name}.md`
- `plugins/pipelines/{name}/README_CN.md` -> `docs/plugins/pipelines/{name}.zh.md`
- `plugins/tools/{name}/README.md` -> `docs/plugins/tools/{name}.md`
- `plugins/tools/{name}/README_CN.md` -> `docs/plugins/tools/{name}.zh.md`
### 4. PR Quality and Maintainer Signal
Check whether the PR description is missing key maintainer context:
- what changed
- why it changed
- whether users need migration or reconfiguration
Only mention this if the omission makes review materially harder.
## Severity Model
Use three levels only:
- `Blocking`: likely bug, release regression, missing required sync, or standards breakage
- `Important`: should be fixed before merge, but not an obvious runtime break
- `Minor`: worthwhile suggestion, but optional
Do not invent issues just to leave a comment.
## Commenting Rules
Leave **one summary comment** only if there is actionable feedback.
The comment must:
- be in English
- be concise and maintainer-like
- lead with findings, not compliments
- include clickable file references like ``plugins/pipes/foo/foo.py`` or ``docs/plugins/pipes/index.md``
- avoid nested bullets
- avoid repeating obvious diff content
Use this exact structure when commenting:
```markdown
## PR Maintainer Review
### Blocking
- `path/to/file`: specific issue and why it matters
### Important
- `path/to/file`: specific issue and what sync/check is missing
### Minor
- `path/to/file`: optional improvement or consistency note
### Merge Readiness
- Ready after the items above are addressed.
```
Rules:
- Omit empty sections.
- If there is only one severity category, include only that category plus `Merge Readiness`.
- Keep the full comment under about 250 words unless multiple files are involved.
## No-Comment Rule
If the PR has no meaningful maintainer findings:
- do not leave a praise-only comment
- do not restate that checks passed
- call `noop` with a short explanation like:
```json
{"noop": {"message": "No action needed: reviewed the PR diff and repository sync expectations, and found no actionable maintainer feedback."}}
```
## Suggested Review Process
1. Identify the changed files in the PR.
2. Read the high-priority repository rule files.
3. Compare changed plugin code against plugin review instructions.
4. Compare changed README or docs files against doc-mirror expectations.
5. Determine whether version-sync or release-facing files are missing.
6. Draft the shortest useful maintainer summary.
7. Leave exactly one `add_comment` or one `noop`.
## Important Constraints
- Do not request broad refactors unless the PR already touches that area.
- Do not require release-prep steps for tiny internal-only edits.
- Do not insist on docs sync when the change is clearly private/internal and not user-facing.
- Prefer precise, repository-specific feedback over generic code review advice.
- If you are unsure whether a sync file is required, downgrade to `Important` rather than `Blocking`.
- If a finding depends on intent that is not visible in the PR, explicitly say it is conditional instead of presenting it as certain.
## Final Requirement
You **must** finish with exactly one safe output action:
- `add_comment` if there is actionable feedback
- `noop` if there is not

View File

@@ -0,0 +1,248 @@
---
description: "Release preflight review for version sync, bilingual docs, release notes, and release-facing consistency"
private: true
labels: [automation, review, release, gh-aw]
metadata:
author: Fu-Jie
category: maintenance
maturity: draft
on:
pull_request:
types: [opened, reopened, synchronize, ready_for_review]
paths:
- 'plugins/**/*.py'
- 'plugins/**/README.md'
- 'plugins/**/README_CN.md'
- 'plugins/**/v*.md'
- 'plugins/**/v*_CN.md'
- 'docs/plugins/**/*.md'
- 'README.md'
- 'README_CN.md'
- '.github/**'
forks: ["*"]
workflow_dispatch:
roles: all
skip-bots: [github-actions, copilot, dependabot, renovate]
permissions:
contents: read
issues: read
pull-requests: read
engine: copilot
network:
allowed:
- defaults
safe-outputs:
add-comment:
target: triggering
max: 1
hide-older-comments: true
footer: false
allowed-github-references: [repo]
timeout-minutes: 12
tools:
github:
toolsets: [repos, issues, pull_requests]
bash:
- pwd
- ls
- cat
- head
- tail
- grep
- wc
- rg
- git status
- git diff
- git show
- git ls-files
---
# Release Preflight Review
You are the repository maintainer assistant for `Fu-Jie/openwebui-extensions`.
Your job is to perform a **release-preflight review** for the triggering change and leave **one concise summary comment only when there is actionable release-facing feedback**.
If the change is not actually release-prep, or it already looks consistent enough that there is no useful maintainer feedback to add, you **must call `noop`** with a short explanation.
## Primary Goal
Review the change for:
- version-sync completeness
- bilingual README and docs consistency
- release-notes completeness
- release-facing index or badge drift
- missing migration or maintainer context for a user-visible release
This workflow is **review-only**. Do not modify files, push code, create releases, or open pull requests.
## High-Priority Source Files
Use these files as the authoritative rule set before forming conclusions:
- `.github/copilot-instructions.md`
- `.github/instructions/commit-message.instructions.md`
- `.github/skills/release-prep/SKILL.md`
- `.github/skills/doc-mirror-sync/SKILL.md`
- `.github/workflows/release.yml`
- `docs/development/gh-aw-integration-plan.md`
- `docs/development/gh-aw-integration-plan.zh.md`
## Review Scope
Start from the PR diff and changed files only. Expand into related release-facing files only when needed to verify sync.
Prioritize repository release policy over generic release advice. This workflow should act like a maintainer performing a final consistency pass before a release-oriented merge.
Focus especially on these areas:
### 1. Version Sync Across Release Files
When a plugin release is being prepared, check whether the expected version bump is consistently reflected across the release-facing file set:
- plugin Python docstring `version:`
- plugin-local `README.md`
- plugin-local `README_CN.md`
- docs mirror page in `docs/plugins/**`
- Chinese docs mirror page in `docs/plugins/**/*.zh.md`
- plugin list entries or badges in `docs/plugins/{type}/index.md`
- plugin list entries or badges in `docs/plugins/{type}/index.zh.md`
Only flag this when the change is clearly release-oriented, version-oriented, or user-visible enough that a synchronized release update is expected.
### 2. README and Docs Mirror Consistency
When plugin README files change, check whether the mirrored docs pages were updated consistently.
Useful path mappings:
- `plugins/actions/{name}/README.md` -> `docs/plugins/actions/{name}.md`
- `plugins/actions/{name}/README_CN.md` -> `docs/plugins/actions/{name}.zh.md`
- `plugins/filters/{name}/README.md` -> `docs/plugins/filters/{name}.md`
- `plugins/filters/{name}/README_CN.md` -> `docs/plugins/filters/{name}.zh.md`
- `plugins/pipes/{name}/README.md` -> `docs/plugins/pipes/{name}.md`
- `plugins/pipes/{name}/README_CN.md` -> `docs/plugins/pipes/{name}.zh.md`
- `plugins/pipelines/{name}/README.md` -> `docs/plugins/pipelines/{name}.md`
- `plugins/pipelines/{name}/README_CN.md` -> `docs/plugins/pipelines/{name}.zh.md`
- `plugins/tools/{name}/README.md` -> `docs/plugins/tools/{name}.md`
- `plugins/tools/{name}/README_CN.md` -> `docs/plugins/tools/{name}.zh.md`
Do not over-report if the change is intentionally docs-only and not a release-prep change.
### 3. What's New and Release Notes Coverage
When a release-facing plugin update is present, check whether the release documentation covers the current scope clearly enough:
- the current `What's New` section reflects the latest release only
- the Chinese `最新更新` section is aligned with the English version
- `v{version}.md` and `v{version}_CN.md` exist when release notes are expected
- release notes cover meaningful feature, fix, docs, or migration changes in the current diff
Do not require release notes for tiny internal-only edits. Do flag missing release notes if the PR is obviously preparing a published plugin release.
### 4. Root Readme and Release-Facing Index Drift
For clearly release-oriented changes, check whether repository-level release-facing surfaces also need updates:
- root `README.md` updated date badge
- root `README_CN.md` updated date badge
- plugin index entries under `docs/plugins/**/index.md`
- plugin index entries under `docs/plugins/**/index.zh.md`
Only mention missing root-level updates when the PR is truly release-prep oriented, not for routine internal edits.
### 5. Maintainer Context and Release Clarity
Check whether the PR description or visible release-facing text is missing essential context:
- what is being released
- why the release matters
- whether migration or reconfiguration is needed
Only mention this if the omission makes release review materially harder.
## Severity Model
Use three levels only:
- `Blocking`: likely release regression, missing required version sync, or clearly incomplete release-facing update
- `Important`: should be fixed before merge to avoid release confusion or drift
- `Minor`: worthwhile release-facing cleanup or consistency suggestion
Do not invent issues just to leave a comment.
## Commenting Rules
Leave **one summary comment** only if there is actionable release-preflight feedback.
The comment must:
- be in English
- be concise and maintainer-like
- lead with findings, not compliments
- include clickable file references like ``plugins/pipes/foo/README.md`` or ``docs/plugins/pipes/index.md``
- avoid nested bullets
- avoid restating obvious diff content
Use this exact structure when commenting:
```markdown
## Release Preflight Review
### Blocking
- `path/to/file`: specific release-facing problem and why it matters
### Important
- `path/to/file`: missing sync or release-documentation gap
### Minor
- `path/to/file`: optional cleanup or consistency improvement
### Release Readiness
- Ready after the items above are addressed.
```
Rules:
- Omit empty sections.
- If there is only one severity category, include only that category plus `Release Readiness`.
- Keep the full comment under about 250 words unless multiple files are involved.
## No-Comment Rule
If the change has no meaningful release-preflight findings:
- do not leave a praise-only comment
- do not restate that checks passed
- call `noop` with a short explanation like:
```json
{"noop": {"message": "No action needed: reviewed the release-facing diff, version-sync expectations, and bilingual documentation coverage, and found no actionable preflight feedback."}}
```
## Suggested Review Process
1. Identify whether the change is actually release-oriented.
2. Inspect the changed files in the PR diff.
3. Read the repository release-prep rule files.
4. Check plugin version-sync expectations only where release intent is visible.
5. Check README, README_CN, docs mirrors, indexes, and release notes for drift.
6. Draft the shortest useful maintainer summary.
7. Leave exactly one `add_comment` or one `noop`.
## Important Constraints
- Do not force full release-prep expectations onto tiny internal edits.
- Do not require root README badge updates unless the PR is clearly release-facing.
- Do not ask for release notes if the change is not realistically a release-prep PR.
- Prefer repository-specific sync feedback over generic release advice.
- If you are unsure whether a release-facing sync file is required, downgrade to `Important` rather than `Blocking`.
- If a finding depends on inferred intent, state it conditionally instead of presenting it as certain.
## Final Requirement
You **must** finish with exactly one safe output action:
- `add_comment` if there is actionable feedback
- `noop` if there is not

View File

@@ -5,13 +5,13 @@
# Triggers:
# - Push to main branch when plugins are modified (auto-release)
# - Manual trigger (workflow_dispatch) with custom release notes
# - Push of version tags (v*)
# - Push of plugin version tags (<plugin>-v*)
#
# What it does:
# 1. Detects plugin version changes compared to the last release
# 2. Generates release notes with updated plugin information
# 3. Creates a GitHub Release with plugin files as downloadable assets
# 4. Supports multiple plugin updates in a single release
# 4. Enforces one plugin creation/update per release
name: Plugin Release
@@ -28,13 +28,14 @@ on:
- 'plugins/**/v*_CN.md'
- 'docs/plugins/**/*.md'
tags:
- '*-v*'
- 'v*'
# Manual trigger with inputs
workflow_dispatch:
inputs:
version:
description: 'Release version (e.g., v1.0.0). Leave empty for auto-generated version.'
description: 'Release tag (e.g., markdown-normalizer-v1.2.8). Leave empty for auto-generated tag.'
required: false
type: string
release_title:
@@ -57,15 +58,23 @@ permissions:
jobs:
check-changes:
runs-on: ubuntu-latest
# Skip release if commit message contains [skip release]
if: ${{ !contains(github.event.head_commit.message, '[skip release]') }}
env:
LANG: en_US.UTF-8
LC_ALL: en_US.UTF-8
outputs:
has_changes: ${{ steps.detect.outputs.has_changes }}
changed_plugins: ${{ steps.detect.outputs.changed_plugins }}
changed_plugin_title: ${{ steps.detect.outputs.changed_plugin_title }}
changed_plugin_slug: ${{ steps.detect.outputs.changed_plugin_slug }}
changed_plugin_version: ${{ steps.detect.outputs.changed_plugin_version }}
changed_plugin_count: ${{ steps.detect.outputs.changed_plugin_count }}
release_notes: ${{ steps.detect.outputs.release_notes }}
has_doc_changes: ${{ steps.detect.outputs.has_doc_changes }}
changed_doc_files: ${{ steps.detect.outputs.changed_doc_files }}
previous_release_tag: ${{ steps.detect.outputs.previous_release_tag }}
compare_ref: ${{ steps.detect.outputs.compare_ref }}
steps:
- name: Checkout repository
@@ -87,16 +96,25 @@ jobs:
- name: Detect plugin changes
id: detect
run: |
# Get the last release tag
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
if [ -z "$LAST_TAG" ]; then
echo "No previous release found, treating all plugins as new"
COMPARE_REF="$(git rev-list --max-parents=0 HEAD)"
else
echo "Comparing with last release: $LAST_TAG"
COMPARE_REF="$LAST_TAG"
# Always compare against the most recent previously released version.
CURRENT_TAG=""
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
CURRENT_TAG="${GITHUB_REF#refs/tags/}"
echo "Current tag event detected: $CURRENT_TAG"
fi
PREVIOUS_RELEASE_TAG=$(git tag --sort=-creatordate | grep -Fxv "$CURRENT_TAG" | head -n1 || true)
if [ -n "$PREVIOUS_RELEASE_TAG" ]; then
echo "Comparing with previous release tag: $PREVIOUS_RELEASE_TAG"
COMPARE_REF="$PREVIOUS_RELEASE_TAG"
else
COMPARE_REF="$(git rev-list --max-parents=0 HEAD)"
echo "No previous release tag found, using repository root commit: $COMPARE_REF"
fi
echo "previous_release_tag=$PREVIOUS_RELEASE_TAG" >> "$GITHUB_OUTPUT"
echo "compare_ref=$COMPARE_REF" >> "$GITHUB_OUTPUT"
# Get current plugin versions
python scripts/extract_plugin_versions.py --json --output current_versions.json
@@ -144,53 +162,97 @@ jobs:
# Check if there are any changes
if grep -q "No changes detected" changes.md; then
if [ -s changed_docs.txt ]; then
echo "has_changes=true" >> $GITHUB_OUTPUT
else
echo "has_changes=false" >> $GITHUB_OUTPUT
fi
# Only trigger release if there are actual version changes, not just doc changes
echo "has_changes=false" >> $GITHUB_OUTPUT
echo "changed_plugins=" >> $GITHUB_OUTPUT
echo "changed_plugin_title=" >> $GITHUB_OUTPUT
echo "changed_plugin_slug=" >> $GITHUB_OUTPUT
echo "changed_plugin_version=" >> $GITHUB_OUTPUT
echo "changed_plugin_count=0" >> $GITHUB_OUTPUT
else
echo "has_changes=true" >> $GITHUB_OUTPUT
# Extract changed plugin file paths using Python
python3 -c "
# Extract changed plugin metadata and enforce a single-plugin release.
python3 <<'PY'
import json
with open('changes.json', 'r') as f:
data = json.load(f)
files = []
import sys
from pathlib import Path
data = json.load(open('changes.json', 'r', encoding='utf-8'))
def get_plugin_meta(plugin):
manifest = plugin.get('data', {}).get('function', {}).get('meta', {}).get('manifest', {})
title = (manifest.get('title') or plugin.get('title') or '').strip()
version = (manifest.get('version') or plugin.get('version') or '').strip()
file_path = (plugin.get('file_path') or '').strip()
slug = Path(file_path).parent.name.replace('_', '-').strip() if file_path else ''
return {
'title': title,
'slug': slug,
'version': version,
'file_path': file_path,
}
plugins = []
seen_keys = set()
for plugin in data.get('added', []):
if 'file_path' in plugin:
files.append(plugin['file_path'])
meta = get_plugin_meta(plugin)
key = meta['file_path'] or meta['title']
if key and key not in seen_keys:
plugins.append(meta)
seen_keys.add(key)
for update in data.get('updated', []):
if 'current' in update and 'file_path' in update['current']:
files.append(update['current']['file_path'])
print('\n'.join(files))
" > changed_files.txt
meta = get_plugin_meta(update.get('current', {}))
key = meta['file_path'] or meta['title']
if key and key not in seen_keys:
plugins.append(meta)
seen_keys.add(key)
Path('changed_files.txt').write_text(
'\n'.join(meta['file_path'] for meta in plugins if meta['file_path']),
encoding='utf-8',
)
Path('changed_plugin_count.txt').write_text(str(len(plugins)), encoding='utf-8')
if len(plugins) > 1:
print('Error: release workflow only supports one plugin creation/update per release.', file=sys.stderr)
for meta in plugins:
print(
f"- {meta['title'] or 'Unknown'} v{meta['version'] or '?'} ({meta['file_path'] or 'unknown path'})",
file=sys.stderr,
)
sys.exit(1)
selected = plugins[0] if plugins else {'title': '', 'slug': '', 'version': ''}
Path('changed_plugin_title.txt').write_text(selected['title'], encoding='utf-8')
Path('changed_plugin_slug.txt').write_text(selected['slug'], encoding='utf-8')
Path('changed_plugin_version.txt').write_text(selected['version'], encoding='utf-8')
PY
echo "changed_plugins<<EOF" >> $GITHUB_OUTPUT
cat changed_files.txt >> $GITHUB_OUTPUT
echo "" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
echo "changed_plugin_title=$(cat changed_plugin_title.txt)" >> $GITHUB_OUTPUT
echo "changed_plugin_slug=$(cat changed_plugin_slug.txt)" >> $GITHUB_OUTPUT
echo "changed_plugin_version=$(cat changed_plugin_version.txt)" >> $GITHUB_OUTPUT
echo "changed_plugin_count=$(cat changed_plugin_count.txt)" >> $GITHUB_OUTPUT
fi
# Store release notes
{
echo 'release_notes<<EOF'
cat changes.md
if [ -s changed_docs.txt ]; then
echo ""
echo "## Documentation Changes"
echo ""
sed 's/^/- /' changed_docs.txt
fi
echo ""
echo 'EOF'
} >> $GITHUB_OUTPUT
release:
needs: check-changes
if: needs.check-changes.outputs.has_changes == 'true' || github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v')
if: needs.check-changes.outputs.has_changes == 'true' || github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
env:
LANG: en_US.UTF-8
@@ -218,35 +280,40 @@ jobs:
id: version
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CHANGED_PLUGIN_SLUG: ${{ needs.check-changes.outputs.changed_plugin_slug }}
CHANGED_PLUGIN_VERSION: ${{ needs.check-changes.outputs.changed_plugin_version }}
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ -n "${{ github.event.inputs.version }}" ]; then
VERSION="${{ github.event.inputs.version }}"
elif [[ "${{ github.ref }}" == refs/tags/v* ]]; then
elif [[ "${{ github.ref }}" == refs/tags/* ]]; then
VERSION="${GITHUB_REF#refs/tags/}"
elif [ -n "$CHANGED_PLUGIN_SLUG" ] && [ -n "$CHANGED_PLUGIN_VERSION" ]; then
VERSION="${CHANGED_PLUGIN_SLUG}-v${CHANGED_PLUGIN_VERSION}"
else
# Auto-generate version based on date and daily release count
TODAY=$(date +'%Y.%m.%d')
TODAY_PREFIX="v${TODAY}-"
# Count existing releases with today's date prefix
# grep -c returns 1 if count is 0, so we use || true to avoid script failure
EXISTING_COUNT=$(gh release list --limit 100 2>/dev/null | grep -c "^${TODAY_PREFIX}" || true)
# Clean up output (handle potential newlines or fallback issues)
EXISTING_COUNT=$(echo "$EXISTING_COUNT" | tr -cd '0-9')
if [ -z "$EXISTING_COUNT" ]; then EXISTING_COUNT=0; fi
NEXT_NUM=$((EXISTING_COUNT + 1))
VERSION="${TODAY_PREFIX}${NEXT_NUM}"
# Final fallback to ensure VERSION is never empty
if [ -z "$VERSION" ]; then
VERSION="v$(date +'%Y.%m.%d-%H%M%S')"
fi
echo "Error: failed to determine plugin-scoped release tag." >&2
exit 1
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Release version: $VERSION"
echo "Release tag: $VERSION"
- name: Build release metadata
id: meta
env:
VERSION: ${{ steps.version.outputs.version }}
INPUT_TITLE: ${{ github.event.inputs.release_title }}
CHANGED_PLUGIN_TITLE: ${{ needs.check-changes.outputs.changed_plugin_title }}
CHANGED_PLUGIN_VERSION: ${{ needs.check-changes.outputs.changed_plugin_version }}
run: |
if [ -n "$INPUT_TITLE" ]; then
RELEASE_NAME="$INPUT_TITLE"
elif [ -n "$CHANGED_PLUGIN_TITLE" ] && [ -n "$CHANGED_PLUGIN_VERSION" ]; then
RELEASE_NAME="$CHANGED_PLUGIN_TITLE v$CHANGED_PLUGIN_VERSION"
else
RELEASE_NAME="$VERSION"
fi
echo "release_name=$RELEASE_NAME" >> "$GITHUB_OUTPUT"
echo "Release name: $RELEASE_NAME"
- name: Extract plugin versions
id: plugins
@@ -341,13 +408,16 @@ jobs:
- name: Get commit messages
id: commits
if: github.event_name == 'push'
env:
PREVIOUS_RELEASE_TAG: ${{ needs.check-changes.outputs.previous_release_tag }}
COMPARE_REF: ${{ needs.check-changes.outputs.compare_ref }}
run: |
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
if [ -n "$LAST_TAG" ]; then
COMMITS=$(git log ${LAST_TAG}..HEAD --pretty=format:"- %s" --no-merges -- plugins/ | head -20)
if [ -n "$PREVIOUS_RELEASE_TAG" ]; then
COMMITS=$(git log ${PREVIOUS_RELEASE_TAG}..HEAD --pretty=format:"- **%s**%n%b" --no-merges -- plugins/ | sed '/^$/d' | head -40)
elif [ -n "$COMPARE_REF" ]; then
COMMITS=$(git log ${COMPARE_REF}..HEAD --pretty=format:"- **%s**%n%b" --no-merges -- plugins/ | sed '/^$/d' | head -40)
else
COMMITS=$(git log --pretty=format:"- %s" --no-merges -10 -- plugins/)
COMMITS=$(git log --pretty=format:"- **%s**%n%b" --no-merges -10 -- plugins/ | sed '/^$/d')
fi
{
@@ -363,13 +433,38 @@ jobs:
VERSION: ${{ steps.version.outputs.version }}
TITLE: ${{ github.event.inputs.release_title }}
NOTES: ${{ github.event.inputs.release_notes }}
CHANGED_PLUGIN_TITLE: ${{ needs.check-changes.outputs.changed_plugin_title }}
CHANGED_PLUGIN_VERSION: ${{ needs.check-changes.outputs.changed_plugin_version }}
DETECTED_CHANGES: ${{ needs.check-changes.outputs.release_notes }}
COMMITS: ${{ steps.commits.outputs.commits }}
DOC_FILES: ${{ needs.check-changes.outputs.changed_doc_files }}
run: |
> release_notes.md
if [ -n "$TITLE" ]; then
if [ -n "$CHANGED_PLUGIN_TITLE" ] && [ -n "$CHANGED_PLUGIN_VERSION" ]; then
echo "# $CHANGED_PLUGIN_TITLE v$CHANGED_PLUGIN_VERSION" >> release_notes.md
echo "" >> release_notes.md
elif [ -n "$TITLE" ]; then
echo "# $TITLE" >> release_notes.md
echo "" >> release_notes.md
fi
# 1. Release notes from v*.md files (highest priority, shown first)
if [ -n "$DOC_FILES" ]; then
RELEASE_NOTE_FILES=$(echo "$DOC_FILES" | grep -E '^plugins/.*/v[^/]*\.md$' | grep -v '_CN\.md$' || true)
if [ -n "$RELEASE_NOTE_FILES" ]; then
while IFS= read -r file; do
[ -z "$file" ] && continue
if [ -f "$file" ]; then
python3 -c "import pathlib, re; file_path = pathlib.Path(r'''$file'''); text = file_path.read_text(encoding='utf-8'); text = re.sub(r'^#\\s+.+?(?:\\r?\\n)+', '', text, count=1, flags=re.MULTILINE); print(text.lstrip().rstrip())" >> release_notes.md
echo "" >> release_notes.md
fi
done <<< "$RELEASE_NOTE_FILES"
fi
fi
# 2. Plugin version changes detected by script
if [ -z "$CHANGED_PLUGIN_TITLE" ] && [ -z "$CHANGED_PLUGIN_VERSION" ] && [ -n "$TITLE" ]; then
echo "## $TITLE" >> release_notes.md
echo "" >> release_notes.md
fi
@@ -381,6 +476,7 @@ jobs:
echo "" >> release_notes.md
fi
# 3. Commits (Conventional Commits format with body)
if [ -n "$COMMITS" ]; then
echo "## Commits" >> release_notes.md
echo "" >> release_notes.md
@@ -394,36 +490,6 @@ jobs:
echo "$NOTES" >> release_notes.md
echo "" >> release_notes.md
fi
if [ -n "$DOC_FILES" ]; then
echo "## Documentation Content" >> release_notes.md
echo "" >> release_notes.md
# Prefer release-note files (v*.md / v*_CN.md), then include other doc files
RELEASE_NOTE_FILES=$(echo "$DOC_FILES" | grep -E '^plugins/.*/v[^/]*\.md$' || true)
OTHER_DOC_FILES=$(echo "$DOC_FILES" | grep -Ev '^plugins/.*/v[^/]*\.md$' || true)
if [ -n "$RELEASE_NOTE_FILES" ]; then
while IFS= read -r file; do
[ -z "$file" ] && continue
if [ -f "$file" ]; then
echo "### ${file}" >> release_notes.md
echo "" >> release_notes.md
cat "$file" >> release_notes.md
echo "" >> release_notes.md
fi
done <<< "$RELEASE_NOTE_FILES"
fi
if [ -n "$OTHER_DOC_FILES" ]; then
echo "### Additional Documentation Files" >> release_notes.md
echo "" >> release_notes.md
echo "$OTHER_DOC_FILES" | sed 's/^/- /' >> release_notes.md
echo "" >> release_notes.md
fi
fi
cat >> release_notes.md << 'EOF'
@@ -450,12 +516,12 @@ jobs:
📚 [Documentation](https://fu-jie.github.io/openwebui-extensions/)
🐛 [Report Issues](https://github.com/Fu-Jie/openwebui-extensions/issues)
EOF
echo "=== Release Notes ==="
cat release_notes.md
- name: Create Git Tag
if: ${{ !startsWith(github.ref, 'refs/tags/v') }}
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
run: |
VERSION="${{ steps.version.outputs.version }}"
@@ -479,7 +545,7 @@ jobs:
with:
tag_name: ${{ steps.version.outputs.version }}
target_commitish: ${{ github.sha }}
name: ${{ github.event.inputs.release_title || steps.version.outputs.version }}
name: ${{ steps.meta.outputs.release_name }}
body_path: release_notes.md
prerelease: ${{ github.event.inputs.prerelease || false }}
make_latest: true

View File

@@ -21,6 +21,7 @@ Plugin types: `actions` / `filters` / `pipes` / `pipelines` / `tools`
2. **No silent failures.** All errors must surface via `__event_emitter__` notification or backend `logging`.
3. **No hardcoded model IDs.** Default to the current conversation model; let `Valves` override.
4. **Chinese responses.** Reply in Simplified Chinese for all planning, explanations, and status summaries. English only for code, commit messages, and docstrings.
5. **Knowledge capture.** Whenever you discover a non-obvious pattern, gotcha, or workaround (e.g., internal API contracts, mock object requirements, parameter injection quirks), save it to `.agent/learnings/{topic}.md` **before ending the session**. See `.agent/learnings/README.md` for format and existing entries.
---

104
ISSUE_57_ANALYSIS_REPORT.md Normal file
View File

@@ -0,0 +1,104 @@
# Markdown Normalizer 插件可靠性修复分析报告 (Issue #57)
## 1. 问题背景
根据 Issue #57 报告,`Markdown Normalizer` 在 v1.2.7 版本中存在数项严重影响可靠性的 Bug包括错误回滚失效、对内联技术内容的过度转义、配置项不生效以及调试日志潜在的隐私风险。
## 2. 核心处理流程图 (v1.2.8)
以下流程展示了插件如何在确保“不损坏原始内容”的前提下进行智能修复:
```mermaid
graph TD
Start([开始处理内容]) --> Cache[1. 内存中存入原始快照 Snapshot]
Cache --> Logic{进入修复流程}
subgraph "分层保护逻辑 (Context-Aware)"
Logic --> Block[识别并锁定 ``` 代码块]
Block --> Inline[识别并锁定 ` 行内代码]
Inline --> Math[识别并锁定 $ LaTeX 公式]
Math --> Clean[仅对非锁定区域执行转义清理]
end
Clean --> Others[执行其他规则: Thought/Details/Table等]
Others --> Check{运行是否报错?}
Check -- 否 (成功) --> Success[返回修复后的内容]
Check -- 是 (失败) --> Rollback[触发回滚: 丢弃所有修改]
Rollback --> Original[返回步骤1存储的原始快照]
Success --> End([输出结果])
Original --> End
```
## 3. 修复项详细说明
### 2.1 错误回滚机制修复 (Reliability: Error Fallback)
- **问题**:在 `normalize` 流程中,如果某个清理器抛出异常,返回的是已被部分修改的 `content`,导致输出内容损坏。
- **技术实现**
```python
def normalize(self, content: str) -> str:
original_content = content # 1. 流程开始前缓存原始快照
try:
# ... 执行一系列清理步骤 ...
return content
except Exception as e:
# 2. 任何步骤失败,立即记录日志并回滚
logger.error(f"Content normalization failed: {e}", exc_info=True)
return original_content # 确保返回的是原始快照
```
- **验证结果**:通过模拟 `RuntimeError` 验证,插件现在能 100% 回滚至原始状态。
### 2.2 上下文感知的转义保护 (Context-Aware Escaping)
- **问题**:全局替换导致正文中包含在 `` ` `` 内的代码片段如正则、Windows 路径)被破坏。
- **技术实现**
重构后的 `_fix_escape_characters` 采用了 **“分词保护策略”**,通过多层嵌套分割来确保仅在非代码上下文中进行清理:
```python
def _fix_escape_characters(self, content: str) -> str:
# 层级 1: 以 ``` 分隔代码块
parts = content.split("```")
for i in range(len(parts)):
is_code_block = (i % 2 != 0)
if is_code_block and not self.config.enable_escape_fix_in_code_blocks:
continue # 默认跳过代码块
if not is_code_block:
# 层级 2: 在非代码块正文中,以 ` 分隔内联代码
inline_parts = parts[i].split("`")
for k in range(0, len(inline_parts), 2): # 仅处理非内联代码部分
# 层级 3: 在非内联代码中,以 $ 分隔 LaTeX 公式
sub_parts = inline_parts[k].split("$")
for j in range(0, len(sub_parts), 2):
# 最终:仅在确认为“纯文本”的部分执行 clean_text
sub_parts[j] = clean_text(sub_parts[j])
inline_parts[k] = "$".join(sub_parts)
parts[i] = "`".join(inline_parts)
else:
parts[i] = clean_text(parts[i])
return "```".join(parts)
```
- **验证结果**:测试用例 `Regex: [\n\r]` 和 `C:\Windows` 在正文中保持原样,而普通文本中的 `\\n` 被正确转换。
### 2.3 配置项激活 (Configuration Enforcement)
- **问题**`enable_escape_fix_in_code_blocks` 开关在代码中被定义但未被逻辑引用。
- **修复方案**:在 `_fix_escape_characters` 处理流程中加入对该开关的判断。
- **验证结果**:当开关关闭(默认)时,代码块内容保持不变;开启时,代码块内执行转义修复。
### 2.4 默认日志策略调整 (Privacy & Performance)
- **问题**`show_debug_log` 默认为 `True`,且会将原始内容打印到浏览器控制台。
- **修复方案**:将默认值改为 `False`。
- **验证结果**:新安装或默认配置下不再主动输出全量日志,仅在用户显式开启时用于调试。
## 3. 综合测试覆盖
已建立 `comprehensive_test_markdown_normalizer.py` 测试脚本,覆盖以下场景:
1. **异常抛出回滚**:确保插件“不破坏”原始内容。
2. **内联代码保护**:验证正则和路径字符串的完整性。
3. **代码块开关控制**:验证配置项的有效性。
4. **LaTeX 命令回归测试**:确保 `\times`, `\theta` 等命令不被误触。
5. **复杂嵌套结构**:验证包含 Thought 标签、列表、内联代码及代码块的混合文本处理。
## 4. 结论
`Markdown Normalizer v1.2.8` 已解决 Issue #57 提出的所有核心可靠性问题。插件现在具备“不损坏内容”的防御性编程能力,并能更智能地感知 Markdown 上下文。
---
**报告日期**2026-03-08
**修复版本**v1.2.8

12
ISSUE_57_REPLY.md Normal file
View File

@@ -0,0 +1,12 @@
# Reply to Issue #57
I have addressed these issues in **v1.2.8** with a focus on reliability and a "Safe-by-Default" approach:
1. **Robust Error Rollback (Items 1, 4, 5)**: I implemented a full `try...except` wrapper. If any error occurs during normalization, the plugin now returns the **100% original text**. This ensures that the output is never partially modified or corrupted.
2. **Conservative Escaping (Item 2)**: To avoid breaking technical content like regex or paths, the escape fixer now strictly skips all code blocks, inline code, and LaTeX formulas by default. I have shifted toward an "opt-in" model where aggressive cleaning is disabled unless specifically requested.
3. **Fixed Configuration (Item 3)**: The `enable_escape_fix_in_code_blocks` Valve was intended to handle escaping within code blocks (e.g., for fixing flat SQL output), but there was a bug preventing it from being applied. I have fixed this, and the setting is now fully functional.
4. **Privacy & Reliability**: I have changed the default for `show_debug_log` to `False`. While it was previously enabled by default to help gather feedback and squash bugs during the initial development phase, the plugin has now undergone multiple iterations and reliability enhancements (including the new tiered protection and rollback mechanisms), making it stable enough for a "silent" and private default operation.
**Recommendation**: If you encounter SQL or data blocks that appear on a single line, you can now manually enable `enable_escape_fix_in_code_blocks` in the Valves to fix them safely.
Please update to the latest version via [OpenWebUI Community](https://openwebui.com/functions/baaa8732-9348-40b7-8359-7e009660e23c). Thank you for your valuable feedback!

View File

@@ -19,3 +19,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -5,7 +5,7 @@
English | [中文](./README_CN.md)
A collection of enhancements, plugins, and prompts for [OpenWebUI](https://github.com/open-webui/open-webui), developed and curated for personal use to extend functionality and improve experience.
A collection of enhancements, plugins, and prompts for [open-webui](https://github.com/open-webui/open-webui), developed and curated for personal use to extend functionality and improve experience.
<!-- STATS_START -->
## 📊 Community Stats
@@ -23,13 +23,12 @@ A collection of enhancements, plugins, and prompts for [OpenWebUI](https://githu
### 🔥 Top 6 Popular Plugins
| Rank | Plugin | Version | Downloads | Views | 📅 Updated |
| :---: | :--- | :---: | :---: | :---: | :---: |
| 🥇 | [Smart Mind Map](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a) | ![p1_version](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p1_version.json&style=flat) | ![p1_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p1_dl.json&style=flat) | ![p1_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p1_vw.json&style=flat) | ![updated](https://img.shields.io/badge/2026--02--28-gray?style=flat) |
| 🥈 | [Smart Infographic](https://openwebui.com/posts/smart_infographic_ad6f0c7f) | ![p2_version](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p2_version.json&style=flat) | ![p2_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p2_dl.json&style=flat) | ![p2_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p2_vw.json&style=flat) | ![updated](https://img.shields.io/badge/2026--02--13-gray?style=flat) |
| 🆕 | [GitHub Copilot Official SDK Pipe](https://openwebui.com/posts/github_copilot_official_sdk_pipe_ce96f7b4) | ![p0_version](https://img.shields.io/badge/version-0.9.1-blue?style=flat) | ![p0_dl](https://img.shields.io/badge/downloads-hot-red?style=flat) | ![p0_vw](https://img.shields.io/badge/views-new-green?style=flat) | ![updated](https://img.shields.io/badge/2026--03--04-gray?style=flat) |
| 🥉 | [Markdown Normalizer](https://openwebui.com/posts/markdown_normalizer_baaa8732) | ![p3_version](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p3_version.json&style=flat) | ![p3_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p3_dl.json&style=flat) | ![p3_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p3_vw.json&style=flat) | ![updated](https://img.shields.io/badge/2026--02--28-gray?style=flat) |
| 4️⃣ | [Export to Word Enhanced](https://openwebui.com/posts/export_to_word_enhanced_formatting_fca6a315) | ![p4_version](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p4_version.json&style=flat) | ![p4_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p4_dl.json&style=flat) | ![p4_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p4_vw.json&style=flat) | ![updated](https://img.shields.io/badge/2026--02--13-gray?style=flat) |
| 5️⃣ | [Async Context Compression](https://openwebui.com/posts/async_context_compression_b1655bc8) | ![p5_version](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p5_version.json&style=flat) | ![p5_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p5_dl.json&style=flat) | ![p5_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p5_vw.json&style=flat) | ![updated](https://img.shields.io/badge/2026--02--28-gray?style=flat) |
| 6⃣ | [Export to Excel](https://openwebui.com/posts/export_mulit_table_to_excel_244b8f9d) | ![p6_version](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p6_version.json&style=flat) | ![p6_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p6_dl.json&style=flat) | ![p6_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p6_vw.json&style=flat) | ![updated](https://img.shields.io/badge/2026--02--13-gray?style=flat) |
| 🥇 | [Smart Mind Map](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a) | ![v](https://img.shields.io/badge/v-1.0.0-blue?style=flat) | ![p1_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p1_dl.json&style=flat) | ![p1_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p1_vw.json&style=flat) | ![updated](https://img.shields.io/badge/2026--03--08-gray?style=flat) |
| 🥈 | [Smart Infographic](https://openwebui.com/posts/smart_infographic_ad6f0c7f) | ![v](https://img.shields.io/badge/v-1.5.0-blue?style=flat) | ![p2_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p2_dl.json&style=flat) | ![p2_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p2_vw.json&style=flat) | ![updated](https://img.shields.io/badge/2026--03--08-gray?style=flat) |
| 🥉 | [Markdown Normalizer](https://openwebui.com/posts/markdown_normalizer_baaa8732) | ![v](https://img.shields.io/badge/v-1.2.7-blue?style=flat) | ![p3_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p3_dl.json&style=flat) | ![p3_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p3_vw.json&style=flat) | ![updated](https://img.shields.io/badge/2026--03--08-gray?style=flat) |
| 4 | [Export to Word Enhanced](https://openwebui.com/posts/export_to_word_enhanced_formatting_fca6a315) | ![v](https://img.shields.io/badge/v-0.4.4-blue?style=flat) | ![p4_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p4_dl.json&style=flat) | ![p4_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p4_vw.json&style=flat) | ![updated](https://img.shields.io/badge/2026--03--08-gray?style=flat) |
| 5️⃣ | [Async Context Compression](https://openwebui.com/posts/async_context_compression_b1655bc8) | ![v](https://img.shields.io/badge/v-1.4.0-blue?style=flat) | ![p5_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p5_dl.json&style=flat) | ![p5_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p5_vw.json&style=flat) | ![updated](https://img.shields.io/badge/2026--03--09-gray?style=flat) |
| 6️⃣ | [AI Task Instruction Generator](https://openwebui.com/posts/ai_task_instruction_generator_9bab8b37) | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![p6_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p6_dl.json&style=flat) | ![p6_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p6_vw.json&style=flat) | ![updated](https://img.shields.io/badge/2026--03--08-gray?style=flat) |
### 📈 Total Downloads Trend
![Activity](https://gist.githubusercontent.com/Fu-Jie/db3d95687075a880af6f1fba76d679c6/raw/chart.svg)
@@ -39,15 +38,17 @@ A collection of enhancements, plugins, and prompts for [OpenWebUI](https://githu
## 🌟 Star Features
### 1. [GitHub Copilot Official SDK Pipe](https://openwebui.com/posts/github_copilot_official_sdk_pipe_ce96f7b4) [![Market](https://img.shields.io/badge/Get-Market-blue?style=flat-square&logo=openwebui)](https://openwebui.com/posts/github_copilot_official_sdk_pipe_ce96f7b4) ![Development](https://img.shields.io/badge/status-active--development-orange?style=flat-square)
### 1. [GitHub Copilot Official SDK Pipe](https://openwebui.com/posts/github_copilot_official_sdk_pipe_ce96f7b4) ![v0.10.0](https://img.shields.io/badge/v0.10.0-blue?style=flat-square) ![active-dev](https://img.shields.io/badge/active--dev-orange?style=flat-square) ![downloads](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_aef940e01073e811a311c3a443d9c149_dl.json&style=flat-square) ![views](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_aef940e01073e811a311c3a443d9c149_vw.json&style=flat-square)
**The ultimate autonomous Agent integration for OpenWebUI.** Deeply bridging GitHub Copilot SDK with your OpenWebUI ecosystem. It enables the Agent to autonomously perform **intent recognition**, **web search**, and **context compaction** while reusing your existing tools, skills, and configurations for a professional, full-featured experience.
> [!TIP]
> **No GitHub Copilot subscription required!** Supports **BYOK (Bring Your Own Key)** mode using your own OpenAI/Anthropic API keys.
#### 🚀 Key Leap (v0.9.1+)
#### 🚀 Key Leap (v0.10.0)
- **⌨️ Prompt Enhancement**: Restored native Copilot CLI **Plan Mode** for complex tasks and integrated native SQLite-backed session management for robust state persistence.
- **📋 Live TODO Widget**: Added a compact real-time task tracking widget synchronized with `session.db`, keeping in-progress work visible without cluttering the chat history.
- **🔌 Seamless Ecosystem Integration**: Automatically injects and reuses your OpenWebUI **Tools**, **MCP**, **OpenAPI Servers**, and **Skills**, significantly enhancing the Agent's capabilities through your existing setup.
- **🌐 Language Consistency**: System prompts mandate that Agent output language remains strictly consistent with user input.
- **🧩 Skills Revolution**: Native support for **SKILL directories** and a **Bidirectional Bridge** to OpenWebUI Workspace Skills.
@@ -58,31 +59,34 @@ A collection of enhancements, plugins, and prompts for [OpenWebUI](https://githu
> [!TIP]
> **💡 Pro Tip: Enhanced Visualization**
> We highly recommend asking the Agent to install the [Visual Explainer](https://github.com/nicobailon/visual-explainer) skill during your conversation. It dramatically improves the aesthetics and interactivity of generated **HTML Artifacts**. Simply tell the AI:
> "Please install this skill: https://github.com/nicobailon/visual-explainer" to get started.
> "Please install this skill: <https://github.com/nicobailon/visual-explainer>" to get started.
#### 📺 Demo: Visual Skills & Data Analysis
![GitHub Copilot SDK Skill Demo](https://github.com/Fu-Jie/openwebui-extensions/raw/main/docs/assets/videos/skill.gif)
> *In this demo, the Agent installs a visual enhancement skill and automatically generates an interactive dashboard from World Cup data.*
![GitHub Copilot SDK + Excel Expert Demo](https://github.com/Fu-Jie/openwebui-extensions/raw/main/docs/assets/images/development/worldcup_enhanced_charts.png)
> *Combined with the Excel Expert skill, the Agent can automate complex data cleaning, multi-dimensional statistics, and generate professional data dashboards.*
#### 🌟 Featured Real-World Cases
- **[GitHub Star Forecasting](./docs/plugins/pipes/star-prediction-example.md)**: Automatically parsing CSV data, writing analysis scripts, and generating interactive growth dashboards.
- **[Video Optimization](./docs/plugins/pipes/video-processing-example.md)**: Direct control of system-level tools (FFmpeg) to accelerate and compress media with professional color optimization.
### 2. [Smart Mind Map](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a) [![Market](https://img.shields.io/badge/Get-Market-blue?style=flat-square&logo=openwebui)](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a)
### 2. [Smart Mind Map](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a)
**Experience interactive thinking.** Seamlessly transforms complex chat sessions into structured, clickable mind maps for better visual modeling and rapid idea extraction.
### 3. [Smart Infographic](https://openwebui.com/posts/smart_infographic_ad6f0c7f) [![Market](https://img.shields.io/badge/Get-Market-blue?style=flat-square&logo=openwebui)](https://openwebui.com/posts/smart_infographic_ad6f0c7f)
### 3. [Smart Infographic](https://openwebui.com/posts/smart_infographic_ad6f0c7f)
**Professional data storytelling.** Converts raw information into sleek, boardroom-ready infographics powered by AntV, perfect for summarizing long-form content instantly.
### 4. [Export to Word Enhanced](https://openwebui.com/posts/export_to_word_enhanced_formatting_fca6a315) [![Market](https://img.shields.io/badge/Get-Market-blue?style=flat-square&logo=openwebui)](https://openwebui.com/posts/export_to_word_enhanced_formatting_fca6a315)
### 4. [Export to Word Enhanced](https://openwebui.com/posts/export_to_word_enhanced_formatting_fca6a315)
**High-fidelity reporting.** Export conversation history into professionally formatted Word documents with preserved headers, code blocks, and math formulas.
### 5. [Async Context Compression](https://openwebui.com/posts/async_context_compression_b1655bc8) [![Market](https://img.shields.io/badge/Get-Market-blue?style=flat-square&logo=openwebui)](https://openwebui.com/posts/async_context_compression_b1655bc8)
### 5. [Async Context Compression](https://openwebui.com/posts/async_context_compression_b1655bc8)
**Maximize your context window.** Intelligently compresses chat history using LLM logic to save tokens and costs while maintaining a high-quality reasoning chain.
@@ -103,6 +107,11 @@ Located in the `plugins/` directory, containing Python-based enhancements:
- **Export to Excel** (`export_to_excel`): Exports chat history to Excel files.
- **Export to Word** (`export_to_docx`): Exports chat history to Word documents.
### Tools
- **Smart Mind Map Tool** (`smart-mind-map-tool`): The tool version of Smart Mind Map, enabling AI proactive/autonomous invocation.
- **OpenWebUI Skills Manager Tool** (`openwebui-skills-manager-tool`): Native tool for managing OpenWebUI skills.
### Filters
- **GitHub Copilot SDK Files Filter** (`github_copilot_sdk_files_filter`): Essential companion for Copilot SDK. Bypasses RAG to ensure full file accessibility for Agents.

View File

@@ -20,13 +20,12 @@ OpenWebUI 增强功能集合。包含个人开发与收集的插件、提示词
### 🔥 热门插件 Top 6
| 排名 | 插件 | 版本 | 下载 | 浏览 | 📅 更新 |
| :---: | :--- | :---: | :---: | :---: | :---: |
| 🥇 | [Smart Mind Map](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a) | ![p1_version](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p1_version.json&style=flat) | ![p1_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p1_dl.json&style=flat) | ![p1_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p1_vw.json&style=flat) | ![updated](https://img.shields.io/badge/2026--02--28-gray?style=flat) |
| 🥈 | [Smart Infographic](https://openwebui.com/posts/smart_infographic_ad6f0c7f) | ![p2_version](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p2_version.json&style=flat) | ![p2_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p2_dl.json&style=flat) | ![p2_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p2_vw.json&style=flat) | ![updated](https://img.shields.io/badge/2026--02--13-gray?style=flat) |
| 🆕 | [GitHub Copilot Official SDK Pipe](https://openwebui.com/posts/github_copilot_official_sdk_pipe_ce96f7b4) | ![p0_version](https://img.shields.io/badge/版本-0.9.1-blue?style=flat) | ![p0_dl](https://img.shields.io/badge/下载-热门-red?style=flat) | ![p0_vw](https://img.shields.io/badge/浏览-最新-green?style=flat) | ![updated](https://img.shields.io/badge/2026--03--04-gray?style=flat) |
| 🥉 | [Markdown Normalizer](https://openwebui.com/posts/markdown_normalizer_baaa8732) | ![p3_version](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p3_version.json&style=flat) | ![p3_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p3_dl.json&style=flat) | ![p3_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p3_vw.json&style=flat) | ![updated](https://img.shields.io/badge/2026--02--28-gray?style=flat) |
| 4️⃣ | [Export to Word Enhanced](https://openwebui.com/posts/export_to_word_enhanced_formatting_fca6a315) | ![p4_version](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p4_version.json&style=flat) | ![p4_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p4_dl.json&style=flat) | ![p4_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p4_vw.json&style=flat) | ![updated](https://img.shields.io/badge/2026--02--13-gray?style=flat) |
| 5️⃣ | [Async Context Compression](https://openwebui.com/posts/async_context_compression_b1655bc8) | ![p5_version](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p5_version.json&style=flat) | ![p5_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p5_dl.json&style=flat) | ![p5_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p5_vw.json&style=flat) | ![updated](https://img.shields.io/badge/2026--02--28-gray?style=flat) |
| 6⃣ | [Export to Excel](https://openwebui.com/posts/export_mulit_table_to_excel_244b8f9d) | ![p6_version](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p6_version.json&style=flat) | ![p6_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p6_dl.json&style=flat) | ![p6_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p6_vw.json&style=flat) | ![updated](https://img.shields.io/badge/2026--02--13-gray?style=flat) |
| 🥇 | [Smart Mind Map](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a) | ![v](https://img.shields.io/badge/v-1.0.0-blue?style=flat) | ![p1_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p1_dl.json&style=flat) | ![p1_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p1_vw.json&style=flat) | ![updated](https://img.shields.io/badge/2026--03--08-gray?style=flat) |
| 🥈 | [Smart Infographic](https://openwebui.com/posts/smart_infographic_ad6f0c7f) | ![v](https://img.shields.io/badge/v-1.5.0-blue?style=flat) | ![p2_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p2_dl.json&style=flat) | ![p2_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p2_vw.json&style=flat) | ![updated](https://img.shields.io/badge/2026--03--08-gray?style=flat) |
| 🥉 | [Markdown Normalizer](https://openwebui.com/posts/markdown_normalizer_baaa8732) | ![v](https://img.shields.io/badge/v-1.2.7-blue?style=flat) | ![p3_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p3_dl.json&style=flat) | ![p3_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p3_vw.json&style=flat) | ![updated](https://img.shields.io/badge/2026--03--08-gray?style=flat) |
| 4 | [Export to Word Enhanced](https://openwebui.com/posts/export_to_word_enhanced_formatting_fca6a315) | ![v](https://img.shields.io/badge/v-0.4.4-blue?style=flat) | ![p4_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p4_dl.json&style=flat) | ![p4_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p4_vw.json&style=flat) | ![updated](https://img.shields.io/badge/2026--03--08-gray?style=flat) |
| 5️⃣ | [Async Context Compression](https://openwebui.com/posts/async_context_compression_b1655bc8) | ![v](https://img.shields.io/badge/v-1.4.0-blue?style=flat) | ![p5_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p5_dl.json&style=flat) | ![p5_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p5_vw.json&style=flat) | ![updated](https://img.shields.io/badge/2026--03--09-gray?style=flat) |
| 6️⃣ | [AI Task Instruction Generator](https://openwebui.com/posts/ai_task_instruction_generator_9bab8b37) | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![p6_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p6_dl.json&style=flat) | ![p6_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_p6_vw.json&style=flat) | ![updated](https://img.shields.io/badge/2026--03--08-gray?style=flat) |
### 📈 总下载量累计趋势
![Activity](https://gist.githubusercontent.com/Fu-Jie/db3d95687075a880af6f1fba76d679c6/raw/chart.svg)
@@ -43,8 +42,10 @@ OpenWebUI 增强功能集合。包含个人开发与收集的插件、提示词
> [!TIP]
> **无需 GitHub Copilot 订阅!** 支持 **BYOK (Bring Your Own Key)** 模式,使用你自己的 OpenAI/Anthropic API Key。
#### 🚀 核心进化 (v0.9.1+)
#### 🚀 核心进化 (v0.10.0)
- **⌨️ 提示词增强**:恢复了原生 Copilot CLI **原生计划模式 (Native Plan Mode)**,并集成了基于 SQLite 的原生会话持久化管理,确保复杂任务编排与状态追踪的稳定性。
- **📋 Live TODO 小组件**:新增基于 `session.db` 实时任务状态的紧凑型嵌入式 TODO 小组件,任务进度常驻可见,无需在正文中重复显示全部待办列表。
- **🔌 生态深度注入**: 自动读取并复用 OpenWebUI **工具 (Tools)**、**MCP**、**OpenAPI Server** 与 **技能 (Skills)**,显著增强 Agent 的实战能力。
- **🧩 技能革命**: 原生支持 **SKILL 目录**,并实现与 OpenWebUI **工作区 > Skills** 的深度双向桥接。
- **🛡️ 安全沙箱**: 严格的用户/会话级 **工作区隔离** 与持久化配置环境。
@@ -55,13 +56,16 @@ OpenWebUI 增强功能集合。包含个人开发与收集的插件、提示词
> [!TIP]
> **💡 进阶实战建议**
> 强烈推荐在对话中让 Agent 为其安装 [Visual Explainer](https://github.com/nicobailon/visual-explainer) 技能。该技能能显著提升 **HTML Artifacts** 的美观度与交互深度,只需对 AI 说:
> “请帮我安装这个技能https://github.com/nicobailon/visual-explainer” 即可瞬间启用。
> “请帮我安装这个技能:<https://github.com/nicobailon/visual-explainer”> 即可瞬间启用。
#### 📺 演示:可视化技能与数据分析
![GitHub Copilot SDK 技能演示](https://github.com/Fu-Jie/openwebui-extensions/raw/main/docs/assets/videos/skill.gif)
> *在此演示中Agent 自动安装可视化增强技能,并根据世界杯表格数据瞬间生成交互式看板。*
![GitHub Copilot SDK + Excel 专家演示](https://github.com/Fu-Jie/openwebui-extensions/raw/main/docs/assets/images/development/worldcup_enhanced_charts.png)
> *结合 Excel 专家技能Agent 可以自动化执行复杂的数据清洗、多维度统计并生成专业的数据看板。*
#### 🌟 核心实战案例
- **[GitHub Star 增长预测](./docs/plugins/pipes/star-prediction-example.zh.md)**:自动解析 CSV 数据,编写 Python 分析脚本并生成动态增长看板。
@@ -100,6 +104,11 @@ OpenWebUI 增强功能集合。包含个人开发与收集的插件、提示词
- **Export to Excel** (`export_to_excel`): 将对话内容导出为 Excel 文件。
- **Export to Word** (`export_to_docx`): 将对话内容导出为 Word 文档。
### Tools (工具)
- **智能思维导图工具** (`smart-mind-map-tool`): 思维导图的 Tool 版本,支持 AI 主动/自主调用。
- **OpenWebUI Skills 管理工具** (`openwebui-skills-manager-tool`): 用于管理 OpenWebUI Skills 的原生工具。
### Filters (消息处理)
- **GitHub Copilot SDK Files Filter** (`github_copilot_sdk_files_filter`): Copilot SDK 必备搭档。绕过 RAG确保 Agent 能真正看到你的每一个文件。

99
TEST_CASES_V1.2.8.md Normal file
View File

@@ -0,0 +1,99 @@
# Markdown Normalizer v1.2.8 测试用例集
您可以将以下内容逐个复制到 OpenWebUI 的聊天框中,以验证插件的各项修复功能。
---
## 用例 1验证 SQL 代码块换行修复 (需要手动开启配置)
**测试目的**:验证 `enable_escape_fix_in_code_blocks` 开关是否生效。
**前提条件**:请先在插件 Valves 设置中将 `enable_escape_fix_in_code_blocks` 设置为 **开启 (True)**
**复制以下内容:**
```text
请帮我美化这段 SQL 的排版,使其恢复正常换行:
```sql
SELECT * \n FROM users \n WHERE status = 'active' \n AND created_at > '2024-01-01' \n ORDER BY id DESC;
```
```
**预期效果**SQL 代码块内的 `\n` 消失,变为整齐的多行 SQL 语句。
---
## 用例 2验证上下文感知保护 (防止误伤技术内容)
**测试目的**:验证插件是否能准确识别“纯文本”和“代码区域”,只修复该修复的地方。
**配置要求**:默认配置即可。
**复制以下内容:**
```text
这是一个综合测试用例。
1. 普通文本修复测试:
这是第一行\\n这是第二行你应该看到这里发生了换行
2. 行内代码保护测试(不应被修改):
- 正则表达式:`[\n\r\t]`
- Windows 路径:`C:\Windows\System32\drivers\etc\hosts`
- 转义测试:`\\n` 应该保持字面量。
3. LaTeX 命令保护测试:
这里的数学公式 $\times \theta \nu \sum$ 应该渲染正常,反斜杠不应被修掉。
4. 现代 LaTeX 定界符转换:
\[ E = mc^2 \]
(上面这行应该被自动转换为 $$ 包围的块级公式)
```
**预期效果**
- 第一部分的 `\\n` 成功换行。
- 第二部分反引号 `` ` `` 里的内容原封不动。
- 第三部分的希腊字母公式渲染正常。
- 第四部分的 `\[` 变成了 `$$` 且能正常显示公式。
---
## 用例 3验证思维链与详情标签规范化
**测试目的**:验证对 `<thought>``<details>` 标签的排版优化。
**复制以下内容:**
```text
<thinking>
这是一个正在思考的思维链。
</thinking>
<details>
<summary>点击查看详情</summary>
这里的排版通常容易出错。
</details>
紧接着详情标签的文字(应该和上面有空行隔开)。
```
**预期效果**
- `<thinking>` 标签被统一为 `<thought>`
- `</details>` 标签下方自动注入了空行,防止与正文粘连导致渲染失效。
---
## 用例 4极端压力与回滚测试 (稳定性验证)
**测试目的**:模拟复杂嵌套环境,验证 100% 回滚机制。
**复制以下内容:**
```text
尝试混合所有复杂元素:
- 列表项 1
- 列表项 2 with `inline \\n code`
- $ \text{Math } \alpha $
```sql
-- SQL with nested issue
SELECT 'literal \n string' FROM `table`;
```
<thought>End of test</thought>
```
**预期效果**
- 无论内部处理逻辑多么复杂,插件都应保证输出稳定的结果。
- 如果模拟任何内部崩溃(技术人员可用),消息会回滚至此原始文本,不会导致页面白屏。

Binary file not shown.

After

Width:  |  Height:  |  Size: 818 KiB

View File

@@ -1,7 +1,7 @@
{
"schemaVersion": 1,
"label": "downloads",
"message": "6.4k",
"message": "7.8k",
"color": "blue",
"namedLogo": "openwebui"
}

View File

@@ -1,6 +1,6 @@
{
"schemaVersion": 1,
"label": "followers",
"message": "295",
"message": "315",
"color": "blue"
}

View File

@@ -1,6 +1,6 @@
{
"schemaVersion": 1,
"label": "plugins",
"message": "25",
"message": "27",
"color": "green"
}

View File

@@ -1,6 +1,6 @@
{
"schemaVersion": 1,
"label": "points",
"message": "299",
"message": "329",
"color": "orange"
}

View File

@@ -1,6 +1,6 @@
{
"schemaVersion": 1,
"label": "upvotes",
"message": "254",
"message": "281",
"color": "brightgreen"
}

View File

@@ -1,14 +1,14 @@
{
"total_posts": 25,
"total_downloads": 7058,
"total_views": 75199,
"total_upvotes": 273,
"total_posts": 27,
"total_downloads": 7786,
"total_views": 82342,
"total_upvotes": 281,
"total_downvotes": 4,
"total_saves": 372,
"total_comments": 58,
"total_saves": 398,
"total_comments": 63,
"by_type": {
"tool": 1,
"post": 5,
"post": 6,
"tool": 2,
"pipe": 1,
"filter": 4,
"action": 12,
@@ -23,13 +23,13 @@
"version": "1.0.0",
"author": "Fu-Jie",
"description": "Intelligently analyzes text content and generates interactive mind maps to help users structure and visualize knowledge.",
"downloads": 1426,
"views": 12082,
"upvotes": 26,
"saves": 63,
"comments": 15,
"created_at": "2025-12-31",
"updated_at": "2026-02-28",
"downloads": 1542,
"views": 12996,
"upvotes": 28,
"saves": 66,
"comments": 18,
"created_at": "2025-12-30",
"updated_at": "2026-02-27",
"url": "https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a"
},
{
@@ -39,10 +39,10 @@
"version": "1.5.0",
"author": "Fu-Jie",
"description": "AI-powered infographic generator based on AntV Infographic. Supports professional templates, auto-icon matching, and SVG/PNG downloads.",
"downloads": 1155,
"views": 11609,
"downloads": 1230,
"views": 12309,
"upvotes": 25,
"saves": 45,
"saves": 46,
"comments": 10,
"created_at": "2025-12-28",
"updated_at": "2026-02-13",
@@ -55,13 +55,13 @@
"version": "1.2.7",
"author": "Fu-Jie",
"description": "A content normalizer filter that fixes common Markdown formatting issues in LLM outputs, such as broken code blocks, LaTeX formulas, and list formatting. Including LaTeX command protection.",
"downloads": 661,
"views": 7239,
"downloads": 719,
"views": 7704,
"upvotes": 20,
"saves": 40,
"saves": 42,
"comments": 5,
"created_at": "2026-01-12",
"updated_at": "2026-02-28",
"updated_at": "2026-03-03",
"url": "https://openwebui.com/posts/markdown_normalizer_baaa8732"
},
{
@@ -71,10 +71,10 @@
"version": "0.4.4",
"author": "Fu-Jie",
"description": "Export current conversation from Markdown to Word (.docx) with Mermaid diagrams rendered client-side (Mermaid.js, SVG+PNG), LaTeX math, real hyperlinks, improved tables, syntax highlighting, and blockquote support.",
"downloads": 628,
"views": 4995,
"upvotes": 16,
"saves": 35,
"downloads": 700,
"views": 5399,
"upvotes": 17,
"saves": 37,
"comments": 5,
"created_at": "2026-01-03",
"updated_at": "2026-02-13",
@@ -87,31 +87,15 @@
"version": "1.3.0",
"author": "Fu-Jie",
"description": "Reduces token consumption in long conversations while maintaining coherence through intelligent summarization and message compression.",
"downloads": 619,
"views": 5875,
"downloads": 669,
"views": 6274,
"upvotes": 16,
"saves": 46,
"saves": 47,
"comments": 0,
"created_at": "2025-11-08",
"updated_at": "2026-02-28",
"updated_at": "2026-03-03",
"url": "https://openwebui.com/posts/async_context_compression_b1655bc8"
},
{
"title": "Export to Excel",
"slug": "export_mulit_table_to_excel_244b8f9d",
"type": "action",
"version": "0.3.7",
"author": "Fu-Jie",
"description": "Extracts tables from chat messages and exports them to Excel (.xlsx) files with smart formatting.",
"downloads": 523,
"views": 2898,
"upvotes": 10,
"saves": 9,
"comments": 0,
"created_at": "2025-05-30",
"updated_at": "2026-02-13",
"url": "https://openwebui.com/posts/export_mulit_table_to_excel_244b8f9d"
},
{
"title": "AI Task Instruction Generator",
"slug": "ai_task_instruction_generator_9bab8b37",
@@ -119,29 +103,45 @@
"version": "",
"author": "",
"description": "",
"downloads": 523,
"views": 6055,
"downloads": 583,
"views": 6659,
"upvotes": 9,
"saves": 14,
"saves": 17,
"comments": 0,
"created_at": "2026-01-28",
"updated_at": "2026-01-28",
"url": "https://openwebui.com/posts/ai_task_instruction_generator_9bab8b37"
},
{
"title": "Export to Excel",
"slug": "export_mulit_table_to_excel_244b8f9d",
"type": "action",
"version": "0.3.7",
"author": "Fu-Jie",
"description": "Extracts tables from chat messages and exports them to Excel (.xlsx) files with smart formatting.",
"downloads": 563,
"views": 3153,
"upvotes": 11,
"saves": 11,
"comments": 0,
"created_at": "2025-05-30",
"updated_at": "2026-02-13",
"url": "https://openwebui.com/posts/export_mulit_table_to_excel_244b8f9d"
},
{
"title": "GitHub Copilot Official SDK Pipe",
"slug": "github_copilot_official_sdk_pipe_ce96f7b4",
"type": "pipe",
"version": "0.9.0",
"version": "0.9.1",
"author": "Fu-Jie",
"description": "Integrate GitHub Copilot SDK. Supports dynamic models, multi-turn conversation, streaming, multimodal input, infinite sessions, bidirectional OpenWebUI Skills bridge, and manage_skills tool.",
"downloads": 301,
"views": 4540,
"description": "A powerful Agent SDK integration for OpenWebUI. It deeply bridges GitHub Copilot SDK with OpenWebUI's ecosystem, enabling the Agent to autonomously perform intent recognition, web search, and context compaction. It seamlessly reuses your existing Tools, MCP servers, OpenAPI servers, and Skills for a professional, full-featured experience.",
"downloads": 335,
"views": 4905,
"upvotes": 16,
"saves": 10,
"comments": 6,
"created_at": "2026-01-26",
"updated_at": "2026-02-28",
"updated_at": "2026-03-03",
"url": "https://openwebui.com/posts/github_copilot_official_sdk_pipe_ce96f7b4"
},
{
@@ -151,8 +151,8 @@
"version": "0.2.4",
"author": "Fu-Jie",
"description": "Quickly generates beautiful flashcards from text, extracting key points and categories.",
"downloads": 295,
"views": 4297,
"downloads": 312,
"views": 4448,
"upvotes": 13,
"saves": 20,
"comments": 2,
@@ -160,22 +160,6 @@
"updated_at": "2026-02-13",
"url": "https://openwebui.com/posts/flash_card_65a2ea8f"
},
{
"title": "Deep Dive",
"slug": "deep_dive_c0b846e4",
"type": "action",
"version": "1.0.0",
"author": "Fu-Jie",
"description": "A comprehensive thinking lens that dives deep into any content - from context to logic, insights, and action paths.",
"downloads": 211,
"views": 1699,
"upvotes": 6,
"saves": 14,
"comments": 0,
"created_at": "2026-01-08",
"updated_at": "2026-01-08",
"url": "https://openwebui.com/posts/deep_dive_c0b846e4"
},
{
"title": "OpenWebUI Skills Manager Tool",
"slug": "openwebui_skills_manager_tool_b4bce8e4",
@@ -183,15 +167,31 @@
"version": "",
"author": "",
"description": "",
"downloads": 169,
"views": 2629,
"upvotes": 6,
"saves": 7,
"comments": 0,
"downloads": 303,
"views": 4265,
"upvotes": 7,
"saves": 13,
"comments": 2,
"created_at": "2026-02-28",
"updated_at": "2026-02-28",
"updated_at": "2026-03-05",
"url": "https://openwebui.com/posts/openwebui_skills_manager_tool_b4bce8e4"
},
{
"title": "Deep Dive",
"slug": "deep_dive_c0b846e4",
"type": "action",
"version": "1.0.0",
"author": "Fu-Jie",
"description": "A comprehensive thinking lens that dives deep into any content - from context to logic, insights, and action paths.",
"downloads": 219,
"views": 1764,
"upvotes": 6,
"saves": 15,
"comments": 0,
"created_at": "2026-01-08",
"updated_at": "2026-01-08",
"url": "https://openwebui.com/posts/deep_dive_c0b846e4"
},
{
"title": "导出为Word增强版",
"slug": "导出为_word_支持公式流程图表格和代码块_8a6306c0",
@@ -199,8 +199,8 @@
"version": "0.4.4",
"author": "Fu-Jie",
"description": "将对话导出为 Word (.docx),支持 Mermaid 图表 (客户端渲染 SVG+PNG)、LaTeX 数学公式、真实超链接、增强表格格式、代码高亮和引用块。",
"downloads": 157,
"views": 2732,
"downloads": 165,
"views": 2831,
"upvotes": 14,
"saves": 7,
"comments": 4,
@@ -215,8 +215,8 @@
"version": "0.1.0",
"author": "Fu-Jie",
"description": "Automatically extracts project rules from conversations and injects them into the folder's system prompt.",
"downloads": 106,
"views": 1911,
"downloads": 112,
"views": 1992,
"upvotes": 7,
"saves": 11,
"comments": 0,
@@ -231,13 +231,13 @@
"version": "0.1.3",
"author": "Fu-Jie",
"description": "A specialized filter to bypass OpenWebUI's default RAG for GitHub Copilot SDK models. It moves uploaded files to a safe location ('copilot_files') so the Copilot Pipe can process them natively without interference.",
"downloads": 69,
"views": 2231,
"downloads": 76,
"views": 2311,
"upvotes": 4,
"saves": 1,
"comments": 0,
"created_at": "2026-02-09",
"updated_at": "2026-02-26",
"updated_at": "2026-03-03",
"url": "https://openwebui.com/posts/github_copilot_sdk_files_filter_403a62ee"
},
{
@@ -247,8 +247,8 @@
"version": "1.5.0",
"author": "Fu-Jie",
"description": "基于 AntV Infographic 的智能信息图生成插件。支持多种专业模板,自动图标匹配,并提供 SVG/PNG 下载功能。",
"downloads": 65,
"views": 1370,
"downloads": 68,
"views": 1431,
"upvotes": 10,
"saves": 1,
"comments": 0,
@@ -263,8 +263,8 @@
"version": "0.9.2",
"author": "Fu-Jie",
"description": "智能分析文本内容,生成交互式思维导图,帮助用户结构化和可视化知识。",
"downloads": 50,
"views": 734,
"downloads": 52,
"views": 761,
"upvotes": 6,
"saves": 2,
"comments": 0,
@@ -279,8 +279,8 @@
"version": "1.2.2",
"author": "Fu-Jie",
"description": "通过智能摘要和消息压缩,降低长对话的 token 消耗,同时保持对话连贯性。",
"downloads": 38,
"views": 814,
"downloads": 39,
"views": 838,
"upvotes": 7,
"saves": 5,
"comments": 0,
@@ -288,6 +288,22 @@
"updated_at": "2026-02-13",
"url": "https://openwebui.com/posts/异步上下文压缩_5c0617cb"
},
{
"title": "🧠 Smart Mind Map Tool: Auto-Generate Interactive Knowledge Graphs",
"slug": "smart_mind_map_tool_auto_generate_interactive_know_d25f4e3d",
"type": "tool",
"version": "",
"author": "",
"description": "",
"downloads": 34,
"views": 767,
"upvotes": 2,
"saves": 3,
"comments": 0,
"created_at": "2026-03-04",
"updated_at": "2026-03-05",
"url": "https://openwebui.com/posts/smart_mind_map_tool_auto_generate_interactive_know_d25f4e3d"
},
{
"title": "闪记卡 (Flash Card)",
"slug": "闪记卡生成插件_4a31eac3",
@@ -295,8 +311,8 @@
"version": "0.2.4",
"author": "Fu-Jie",
"description": "快速将文本提炼为精美的学习记忆卡片,支持核心要点提取与分类。",
"downloads": 33,
"views": 863,
"downloads": 34,
"views": 888,
"upvotes": 7,
"saves": 1,
"comments": 0,
@@ -311,8 +327,8 @@
"version": "1.0.0",
"author": "Fu-Jie",
"description": "全方位的思维透镜 —— 从背景全景到逻辑脉络,从深度洞察到行动路径。",
"downloads": 29,
"views": 626,
"downloads": 31,
"views": 647,
"upvotes": 5,
"saves": 1,
"comments": 0,
@@ -320,6 +336,22 @@
"updated_at": "2026-01-08",
"url": "https://openwebui.com/posts/精读_99830b0f"
},
{
"title": "An Unconventional Use of Open Terminal ⚡",
"slug": "an_unconventional_use_of_open_terminal_35498f8f",
"type": "post",
"version": "",
"author": "",
"description": "",
"downloads": 0,
"views": 14,
"upvotes": 1,
"saves": 0,
"comments": 0,
"created_at": "2026-03-06",
"updated_at": "2026-03-06",
"url": "https://openwebui.com/posts/an_unconventional_use_of_open_terminal_35498f8f"
},
{
"title": "🚀 GitHub Copilot SDK Pipe v0.9.0: Skills & RichUI",
"slug": "github_copilot_sdk_pipe_v090_copilot_sdk_skills_co_99a42452",
@@ -328,11 +360,11 @@
"author": "",
"description": "",
"downloads": 0,
"views": 1162,
"views": 1585,
"upvotes": 5,
"saves": 1,
"comments": 0,
"created_at": "2026-02-28",
"created_at": "2026-02-27",
"updated_at": "2026-02-28",
"url": "https://openwebui.com/posts/github_copilot_sdk_pipe_v090_copilot_sdk_skills_co_99a42452"
},
@@ -344,11 +376,11 @@
"author": "",
"description": "",
"downloads": 0,
"views": 2504,
"views": 2608,
"upvotes": 8,
"saves": 2,
"saves": 4,
"comments": 1,
"created_at": "2026-02-23",
"created_at": "2026-02-22",
"updated_at": "2026-02-28",
"url": "https://openwebui.com/posts/github_copilot_sdk_pipe_v070_native_tool_ui_zero_c_4af38131"
},
@@ -360,7 +392,7 @@
"author": "",
"description": "",
"downloads": 0,
"views": 2341,
"views": 2390,
"upvotes": 7,
"saves": 4,
"comments": 0,
@@ -376,12 +408,12 @@
"author": "",
"description": "",
"downloads": 0,
"views": 1887,
"views": 1915,
"upvotes": 12,
"saves": 21,
"comments": 8,
"created_at": "2026-01-25",
"updated_at": "2026-01-29",
"updated_at": "2026-01-28",
"url": "https://openwebui.com/posts/open_webui_prompt_plus_ai_powered_prompt_manager_s_15fa060e"
},
{
@@ -392,7 +424,7 @@
"author": "",
"description": "",
"downloads": 0,
"views": 246,
"views": 251,
"upvotes": 2,
"saves": 0,
"comments": 0,
@@ -408,7 +440,7 @@
"author": "",
"description": "",
"downloads": 0,
"views": 1531,
"views": 1549,
"upvotes": 16,
"saves": 12,
"comments": 2,
@@ -422,11 +454,11 @@
"name": "Fu-Jie",
"profile_url": "https://openwebui.com/u/Fu-Jie",
"profile_image": "https://community.s3.openwebui.com/uploads/users/b15d1348-4347-42b4-b815-e053342d6cb0/profile_d9510745-4bd4-4f8f-a997-4a21847d9300.webp",
"followers": 307,
"followers": 315,
"following": 6,
"total_points": 319,
"post_points": 271,
"comment_points": 48,
"contributions": 54
"total_points": 329,
"post_points": 279,
"comment_points": 50,
"contributions": 59
}
}

View File

@@ -8,7 +8,7 @@
> *Blue: Downloads | Purple: Views (Real-time dynamic)*
### 📂 Content Distribution
![Distribution](https://kroki.io/mermaid/svg/eNpNjEEOQEAMRfdO0cwNCBtrB7BwAaTkJ0ObmSJuz2Dh795r_1cwGcwzudZvM1bqTuVIDaIFDJtBVpfRHWci3lFN-Ysq0RJWH0L5d53gjUMS5Sv68ZlKH8XXCLKo_TqBd_DxiAuzSCeb)
![Distribution](https://kroki.io/mermaid/svg/eNoryExVKMksyUlVUArIKU3PzFMIqSxILVZwySwuKcpMKi3JzM9T4lIAAqWC_OISJQUrBTMItyQ_PwfENYLKZhakgriGEG5aZk5JahFIwAQikJgMNgqkAqajKD-3oARJT1FqWWZqOVgAALS1J50=)
## 📈 Overview
@@ -25,8 +25,8 @@
## 📂 By Type
- ![tool](https://img.shields.io/badge/tool-1-teal)
- ![post](https://img.shields.io/badge/post-5-blue)
- ![post](https://img.shields.io/badge/post-6-blue)
- ![tool](https://img.shields.io/badge/tool-2-teal)
- ![pipe](https://img.shields.io/badge/pipe-1-blueviolet)
- ![filter](https://img.shields.io/badge/filter-4-brightgreen)
- ![action](https://img.shields.io/badge/action-12-orange)
@@ -37,28 +37,30 @@
| Rank | Title | Type | Version | Downloads | Views | Upvotes | Saves | Updated |
|:---:|------|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| 1 | [Smart Mind Map](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a) | action | ![v](https://img.shields.io/badge/v-1.0.0-blue?style=flat) | ![post_207cd433d27fdd853ccbfa941e6fa67f_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_207cd433d27fdd853ccbfa941e6fa67f_dl.json&style=flat) | ![post_207cd433d27fdd853ccbfa941e6fa67f_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_207cd433d27fdd853ccbfa941e6fa67f_vw.json&style=flat) | ![post_207cd433d27fdd853ccbfa941e6fa67f_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_207cd433d27fdd853ccbfa941e6fa67f_up.json&style=flat) | ![post_207cd433d27fdd853ccbfa941e6fa67f_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_207cd433d27fdd853ccbfa941e6fa67f_sv.json&style=flat) | 2026-02-28 |
| 1 | [Smart Mind Map](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a) | action | ![v](https://img.shields.io/badge/v-1.0.0-blue?style=flat) | ![post_207cd433d27fdd853ccbfa941e6fa67f_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_207cd433d27fdd853ccbfa941e6fa67f_dl.json&style=flat) | ![post_207cd433d27fdd853ccbfa941e6fa67f_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_207cd433d27fdd853ccbfa941e6fa67f_vw.json&style=flat) | ![post_207cd433d27fdd853ccbfa941e6fa67f_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_207cd433d27fdd853ccbfa941e6fa67f_up.json&style=flat) | ![post_207cd433d27fdd853ccbfa941e6fa67f_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_207cd433d27fdd853ccbfa941e6fa67f_sv.json&style=flat) | 2026-02-27 |
| 2 | [Smart Infographic](https://openwebui.com/posts/smart_infographic_ad6f0c7f) | action | ![v](https://img.shields.io/badge/v-1.5.0-blue?style=flat) | ![post_376135b87514e63570283b2057459b1f_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_376135b87514e63570283b2057459b1f_dl.json&style=flat) | ![post_376135b87514e63570283b2057459b1f_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_376135b87514e63570283b2057459b1f_vw.json&style=flat) | ![post_376135b87514e63570283b2057459b1f_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_376135b87514e63570283b2057459b1f_up.json&style=flat) | ![post_376135b87514e63570283b2057459b1f_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_376135b87514e63570283b2057459b1f_sv.json&style=flat) | 2026-02-13 |
| 3 | [Markdown Normalizer](https://openwebui.com/posts/markdown_normalizer_baaa8732) | filter | ![v](https://img.shields.io/badge/v-1.2.7-blue?style=flat) | ![post_a963da7c5d914310e7026b8c8a6635b0_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_a963da7c5d914310e7026b8c8a6635b0_dl.json&style=flat) | ![post_a963da7c5d914310e7026b8c8a6635b0_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_a963da7c5d914310e7026b8c8a6635b0_vw.json&style=flat) | ![post_a963da7c5d914310e7026b8c8a6635b0_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_a963da7c5d914310e7026b8c8a6635b0_up.json&style=flat) | ![post_a963da7c5d914310e7026b8c8a6635b0_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_a963da7c5d914310e7026b8c8a6635b0_sv.json&style=flat) | 2026-02-28 |
| 3 | [Markdown Normalizer](https://openwebui.com/posts/markdown_normalizer_baaa8732) | filter | ![v](https://img.shields.io/badge/v-1.2.7-blue?style=flat) | ![post_a963da7c5d914310e7026b8c8a6635b0_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_a963da7c5d914310e7026b8c8a6635b0_dl.json&style=flat) | ![post_a963da7c5d914310e7026b8c8a6635b0_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_a963da7c5d914310e7026b8c8a6635b0_vw.json&style=flat) | ![post_a963da7c5d914310e7026b8c8a6635b0_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_a963da7c5d914310e7026b8c8a6635b0_up.json&style=flat) | ![post_a963da7c5d914310e7026b8c8a6635b0_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_a963da7c5d914310e7026b8c8a6635b0_sv.json&style=flat) | 2026-03-03 |
| 4 | [Export to Word Enhanced](https://openwebui.com/posts/export_to_word_enhanced_formatting_fca6a315) | action | ![v](https://img.shields.io/badge/v-0.4.4-blue?style=flat) | ![post_dc072f01690dc8293384153dc0231d05_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_dc072f01690dc8293384153dc0231d05_dl.json&style=flat) | ![post_dc072f01690dc8293384153dc0231d05_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_dc072f01690dc8293384153dc0231d05_vw.json&style=flat) | ![post_dc072f01690dc8293384153dc0231d05_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_dc072f01690dc8293384153dc0231d05_up.json&style=flat) | ![post_dc072f01690dc8293384153dc0231d05_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_dc072f01690dc8293384153dc0231d05_sv.json&style=flat) | 2026-02-13 |
| 5 | [Async Context Compression](https://openwebui.com/posts/async_context_compression_b1655bc8) | filter | ![v](https://img.shields.io/badge/v-1.3.0-blue?style=flat) | ![post_0640a7ef0970872217cb939f2ba9c12c_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0640a7ef0970872217cb939f2ba9c12c_dl.json&style=flat) | ![post_0640a7ef0970872217cb939f2ba9c12c_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0640a7ef0970872217cb939f2ba9c12c_vw.json&style=flat) | ![post_0640a7ef0970872217cb939f2ba9c12c_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0640a7ef0970872217cb939f2ba9c12c_up.json&style=flat) | ![post_0640a7ef0970872217cb939f2ba9c12c_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0640a7ef0970872217cb939f2ba9c12c_sv.json&style=flat) | 2026-02-28 |
| 6 | [Export to Excel](https://openwebui.com/posts/export_mulit_table_to_excel_244b8f9d) | action | ![v](https://img.shields.io/badge/v-0.3.7-blue?style=flat) | ![post_c5ae05d46c7b999a1e36ca1457f1926b_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c5ae05d46c7b999a1e36ca1457f1926b_dl.json&style=flat) | ![post_c5ae05d46c7b999a1e36ca1457f1926b_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c5ae05d46c7b999a1e36ca1457f1926b_vw.json&style=flat) | ![post_c5ae05d46c7b999a1e36ca1457f1926b_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c5ae05d46c7b999a1e36ca1457f1926b_up.json&style=flat) | ![post_c5ae05d46c7b999a1e36ca1457f1926b_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c5ae05d46c7b999a1e36ca1457f1926b_sv.json&style=flat) | 2026-02-13 |
| 7 | [AI Task Instruction Generator](https://openwebui.com/posts/ai_task_instruction_generator_9bab8b37) | prompt | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_19f469a02b32d21f86b16d1395a81317_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_19f469a02b32d21f86b16d1395a81317_dl.json&style=flat) | ![post_19f469a02b32d21f86b16d1395a81317_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_19f469a02b32d21f86b16d1395a81317_vw.json&style=flat) | ![post_19f469a02b32d21f86b16d1395a81317_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_19f469a02b32d21f86b16d1395a81317_up.json&style=flat) | ![post_19f469a02b32d21f86b16d1395a81317_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_19f469a02b32d21f86b16d1395a81317_sv.json&style=flat) | 2026-01-28 |
| 8 | [GitHub Copilot Official SDK Pipe](https://openwebui.com/posts/github_copilot_official_sdk_pipe_ce96f7b4) | pipe | ![v](https://img.shields.io/badge/v-0.9.0-blue?style=flat) | ![post_aef940e01073e811a311c3a443d9c149_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_aef940e01073e811a311c3a443d9c149_dl.json&style=flat) | ![post_aef940e01073e811a311c3a443d9c149_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_aef940e01073e811a311c3a443d9c149_vw.json&style=flat) | ![post_aef940e01073e811a311c3a443d9c149_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_aef940e01073e811a311c3a443d9c149_up.json&style=flat) | ![post_aef940e01073e811a311c3a443d9c149_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_aef940e01073e811a311c3a443d9c149_sv.json&style=flat) | 2026-02-28 |
| 5 | [Async Context Compression](https://openwebui.com/posts/async_context_compression_b1655bc8) | filter | ![v](https://img.shields.io/badge/v-1.3.0-blue?style=flat) | ![post_0640a7ef0970872217cb939f2ba9c12c_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0640a7ef0970872217cb939f2ba9c12c_dl.json&style=flat) | ![post_0640a7ef0970872217cb939f2ba9c12c_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0640a7ef0970872217cb939f2ba9c12c_vw.json&style=flat) | ![post_0640a7ef0970872217cb939f2ba9c12c_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0640a7ef0970872217cb939f2ba9c12c_up.json&style=flat) | ![post_0640a7ef0970872217cb939f2ba9c12c_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0640a7ef0970872217cb939f2ba9c12c_sv.json&style=flat) | 2026-03-03 |
| 6 | [AI Task Instruction Generator](https://openwebui.com/posts/ai_task_instruction_generator_9bab8b37) | prompt | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_19f469a02b32d21f86b16d1395a81317_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_19f469a02b32d21f86b16d1395a81317_dl.json&style=flat) | ![post_19f469a02b32d21f86b16d1395a81317_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_19f469a02b32d21f86b16d1395a81317_vw.json&style=flat) | ![post_19f469a02b32d21f86b16d1395a81317_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_19f469a02b32d21f86b16d1395a81317_up.json&style=flat) | ![post_19f469a02b32d21f86b16d1395a81317_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_19f469a02b32d21f86b16d1395a81317_sv.json&style=flat) | 2026-01-28 |
| 7 | [Export to Excel](https://openwebui.com/posts/export_mulit_table_to_excel_244b8f9d) | action | ![v](https://img.shields.io/badge/v-0.3.7-blue?style=flat) | ![post_c5ae05d46c7b999a1e36ca1457f1926b_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c5ae05d46c7b999a1e36ca1457f1926b_dl.json&style=flat) | ![post_c5ae05d46c7b999a1e36ca1457f1926b_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c5ae05d46c7b999a1e36ca1457f1926b_vw.json&style=flat) | ![post_c5ae05d46c7b999a1e36ca1457f1926b_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c5ae05d46c7b999a1e36ca1457f1926b_up.json&style=flat) | ![post_c5ae05d46c7b999a1e36ca1457f1926b_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c5ae05d46c7b999a1e36ca1457f1926b_sv.json&style=flat) | 2026-02-13 |
| 8 | [GitHub Copilot Official SDK Pipe](https://openwebui.com/posts/github_copilot_official_sdk_pipe_ce96f7b4) | pipe | ![v](https://img.shields.io/badge/v-0.9.1-blue?style=flat) | ![post_aef940e01073e811a311c3a443d9c149_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_aef940e01073e811a311c3a443d9c149_dl.json&style=flat) | ![post_aef940e01073e811a311c3a443d9c149_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_aef940e01073e811a311c3a443d9c149_vw.json&style=flat) | ![post_aef940e01073e811a311c3a443d9c149_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_aef940e01073e811a311c3a443d9c149_up.json&style=flat) | ![post_aef940e01073e811a311c3a443d9c149_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_aef940e01073e811a311c3a443d9c149_sv.json&style=flat) | 2026-03-03 |
| 9 | [Flash Card](https://openwebui.com/posts/flash_card_65a2ea8f) | action | ![v](https://img.shields.io/badge/v-0.2.4-blue?style=flat) | ![post_5cf7a5dad74ef15ee4ffc6fe10da8213_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_5cf7a5dad74ef15ee4ffc6fe10da8213_dl.json&style=flat) | ![post_5cf7a5dad74ef15ee4ffc6fe10da8213_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_5cf7a5dad74ef15ee4ffc6fe10da8213_vw.json&style=flat) | ![post_5cf7a5dad74ef15ee4ffc6fe10da8213_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_5cf7a5dad74ef15ee4ffc6fe10da8213_up.json&style=flat) | ![post_5cf7a5dad74ef15ee4ffc6fe10da8213_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_5cf7a5dad74ef15ee4ffc6fe10da8213_sv.json&style=flat) | 2026-02-13 |
| 10 | [Deep Dive](https://openwebui.com/posts/deep_dive_c0b846e4) | action | ![v](https://img.shields.io/badge/v-1.0.0-blue?style=flat) | ![post_78e3480de4e6bd4615498495c5d15979_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_78e3480de4e6bd4615498495c5d15979_dl.json&style=flat) | ![post_78e3480de4e6bd4615498495c5d15979_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_78e3480de4e6bd4615498495c5d15979_vw.json&style=flat) | ![post_78e3480de4e6bd4615498495c5d15979_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_78e3480de4e6bd4615498495c5d15979_up.json&style=flat) | ![post_78e3480de4e6bd4615498495c5d15979_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_78e3480de4e6bd4615498495c5d15979_sv.json&style=flat) | 2026-01-08 |
| 11 | [OpenWebUI Skills Manager Tool](https://openwebui.com/posts/openwebui_skills_manager_tool_b4bce8e4) | tool | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_6e9b698faed2ca2ba6f8308635a4804e_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_6e9b698faed2ca2ba6f8308635a4804e_dl.json&style=flat) | ![post_6e9b698faed2ca2ba6f8308635a4804e_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_6e9b698faed2ca2ba6f8308635a4804e_vw.json&style=flat) | ![post_6e9b698faed2ca2ba6f8308635a4804e_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_6e9b698faed2ca2ba6f8308635a4804e_up.json&style=flat) | ![post_6e9b698faed2ca2ba6f8308635a4804e_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_6e9b698faed2ca2ba6f8308635a4804e_sv.json&style=flat) | 2026-02-28 |
| 10 | [OpenWebUI Skills Manager Tool](https://openwebui.com/posts/openwebui_skills_manager_tool_b4bce8e4) | tool | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_6e9b698faed2ca2ba6f8308635a4804e_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_6e9b698faed2ca2ba6f8308635a4804e_dl.json&style=flat) | ![post_6e9b698faed2ca2ba6f8308635a4804e_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_6e9b698faed2ca2ba6f8308635a4804e_vw.json&style=flat) | ![post_6e9b698faed2ca2ba6f8308635a4804e_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_6e9b698faed2ca2ba6f8308635a4804e_up.json&style=flat) | ![post_6e9b698faed2ca2ba6f8308635a4804e_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_6e9b698faed2ca2ba6f8308635a4804e_sv.json&style=flat) | 2026-03-05 |
| 11 | [Deep Dive](https://openwebui.com/posts/deep_dive_c0b846e4) | action | ![v](https://img.shields.io/badge/v-1.0.0-blue?style=flat) | ![post_78e3480de4e6bd4615498495c5d15979_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_78e3480de4e6bd4615498495c5d15979_dl.json&style=flat) | ![post_78e3480de4e6bd4615498495c5d15979_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_78e3480de4e6bd4615498495c5d15979_vw.json&style=flat) | ![post_78e3480de4e6bd4615498495c5d15979_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_78e3480de4e6bd4615498495c5d15979_up.json&style=flat) | ![post_78e3480de4e6bd4615498495c5d15979_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_78e3480de4e6bd4615498495c5d15979_sv.json&style=flat) | 2026-01-08 |
| 12 | [导出为Word增强版](https://openwebui.com/posts/导出为_word_支持公式流程图表格和代码块_8a6306c0) | action | ![v](https://img.shields.io/badge/v-0.4.4-blue?style=flat) | ![post_fc4cbdf0eb78bd98c2ed7650d459b59e_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_fc4cbdf0eb78bd98c2ed7650d459b59e_dl.json&style=flat) | ![post_fc4cbdf0eb78bd98c2ed7650d459b59e_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_fc4cbdf0eb78bd98c2ed7650d459b59e_vw.json&style=flat) | ![post_fc4cbdf0eb78bd98c2ed7650d459b59e_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_fc4cbdf0eb78bd98c2ed7650d459b59e_up.json&style=flat) | ![post_fc4cbdf0eb78bd98c2ed7650d459b59e_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_fc4cbdf0eb78bd98c2ed7650d459b59e_sv.json&style=flat) | 2026-02-13 |
| 13 | [📂 Folder Memory Auto-Evolving Project Context](https://openwebui.com/posts/folder_memory_auto_evolving_project_context_4a9875b2) | filter | ![v](https://img.shields.io/badge/v-0.1.0-blue?style=flat) | ![post_a125b3a3702a07c86c77668dcede2149_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_a125b3a3702a07c86c77668dcede2149_dl.json&style=flat) | ![post_a125b3a3702a07c86c77668dcede2149_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_a125b3a3702a07c86c77668dcede2149_vw.json&style=flat) | ![post_a125b3a3702a07c86c77668dcede2149_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_a125b3a3702a07c86c77668dcede2149_up.json&style=flat) | ![post_a125b3a3702a07c86c77668dcede2149_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_a125b3a3702a07c86c77668dcede2149_sv.json&style=flat) | 2026-01-20 |
| 14 | [GitHub Copilot SDK Files Filter](https://openwebui.com/posts/github_copilot_sdk_files_filter_403a62ee) | filter | ![v](https://img.shields.io/badge/v-0.1.3-blue?style=flat) | ![post_68081377a06a5746efe798136a72c6b6_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_68081377a06a5746efe798136a72c6b6_dl.json&style=flat) | ![post_68081377a06a5746efe798136a72c6b6_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_68081377a06a5746efe798136a72c6b6_vw.json&style=flat) | ![post_68081377a06a5746efe798136a72c6b6_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_68081377a06a5746efe798136a72c6b6_up.json&style=flat) | ![post_68081377a06a5746efe798136a72c6b6_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_68081377a06a5746efe798136a72c6b6_sv.json&style=flat) | 2026-02-26 |
| 14 | [GitHub Copilot SDK Files Filter](https://openwebui.com/posts/github_copilot_sdk_files_filter_403a62ee) | filter | ![v](https://img.shields.io/badge/v-0.1.3-blue?style=flat) | ![post_68081377a06a5746efe798136a72c6b6_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_68081377a06a5746efe798136a72c6b6_dl.json&style=flat) | ![post_68081377a06a5746efe798136a72c6b6_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_68081377a06a5746efe798136a72c6b6_vw.json&style=flat) | ![post_68081377a06a5746efe798136a72c6b6_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_68081377a06a5746efe798136a72c6b6_up.json&style=flat) | ![post_68081377a06a5746efe798136a72c6b6_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_68081377a06a5746efe798136a72c6b6_sv.json&style=flat) | 2026-03-03 |
| 15 | [智能信息图](https://openwebui.com/posts/智能信息图_e04a48ff) | action | ![v](https://img.shields.io/badge/v-1.5.0-blue?style=flat) | ![post_0c3f8621f77a9d5875d3c26ee38a3954_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0c3f8621f77a9d5875d3c26ee38a3954_dl.json&style=flat) | ![post_0c3f8621f77a9d5875d3c26ee38a3954_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0c3f8621f77a9d5875d3c26ee38a3954_vw.json&style=flat) | ![post_0c3f8621f77a9d5875d3c26ee38a3954_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0c3f8621f77a9d5875d3c26ee38a3954_up.json&style=flat) | ![post_0c3f8621f77a9d5875d3c26ee38a3954_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0c3f8621f77a9d5875d3c26ee38a3954_sv.json&style=flat) | 2026-02-13 |
| 16 | [思维导图](https://openwebui.com/posts/智能生成交互式思维导图帮助用户可视化知识_8d4b097b) | action | ![v](https://img.shields.io/badge/v-0.9.2-blue?style=flat) | ![post_c5bfbfd0d29694a04c597e26202b824f_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c5bfbfd0d29694a04c597e26202b824f_dl.json&style=flat) | ![post_c5bfbfd0d29694a04c597e26202b824f_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c5bfbfd0d29694a04c597e26202b824f_vw.json&style=flat) | ![post_c5bfbfd0d29694a04c597e26202b824f_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c5bfbfd0d29694a04c597e26202b824f_up.json&style=flat) | ![post_c5bfbfd0d29694a04c597e26202b824f_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c5bfbfd0d29694a04c597e26202b824f_sv.json&style=flat) | 2026-02-13 |
| 17 | [异步上下文压缩](https://openwebui.com/posts/异步上下文压缩_5c0617cb) | action | ![v](https://img.shields.io/badge/v-1.2.2-blue?style=flat) | ![post_fa447e583483020bc1bf35e74302c75c_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_fa447e583483020bc1bf35e74302c75c_dl.json&style=flat) | ![post_fa447e583483020bc1bf35e74302c75c_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_fa447e583483020bc1bf35e74302c75c_vw.json&style=flat) | ![post_fa447e583483020bc1bf35e74302c75c_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_fa447e583483020bc1bf35e74302c75c_up.json&style=flat) | ![post_fa447e583483020bc1bf35e74302c75c_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_fa447e583483020bc1bf35e74302c75c_sv.json&style=flat) | 2026-02-13 |
| 18 | [闪记卡 (Flash Card)](https://openwebui.com/posts/闪记卡生成插件_4a31eac3) | action | ![v](https://img.shields.io/badge/v-0.2.4-blue?style=flat) | ![post_c68a2593e76ec324361358575b6501de_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c68a2593e76ec324361358575b6501de_dl.json&style=flat) | ![post_c68a2593e76ec324361358575b6501de_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c68a2593e76ec324361358575b6501de_vw.json&style=flat) | ![post_c68a2593e76ec324361358575b6501de_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c68a2593e76ec324361358575b6501de_up.json&style=flat) | ![post_c68a2593e76ec324361358575b6501de_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c68a2593e76ec324361358575b6501de_sv.json&style=flat) | 2026-02-13 |
| 19 | [精读](https://openwebui.com/posts/精读_99830b0f) | action | ![v](https://img.shields.io/badge/v-1.0.0-blue?style=flat) | ![post_14a3b01bc6559558b48a61a56dd3635d_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_14a3b01bc6559558b48a61a56dd3635d_dl.json&style=flat) | ![post_14a3b01bc6559558b48a61a56dd3635d_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_14a3b01bc6559558b48a61a56dd3635d_vw.json&style=flat) | ![post_14a3b01bc6559558b48a61a56dd3635d_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_14a3b01bc6559558b48a61a56dd3635d_up.json&style=flat) | ![post_14a3b01bc6559558b48a61a56dd3635d_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_14a3b01bc6559558b48a61a56dd3635d_sv.json&style=flat) | 2026-01-08 |
| 20 | [🚀 GitHub Copilot SDK Pipe v0.9.0: Skills & RichUI](https://openwebui.com/posts/github_copilot_sdk_pipe_v090_copilot_sdk_skills_co_99a42452) | post | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_0f2ccf8471a3db168298f4f1e86ba0f4_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0f2ccf8471a3db168298f4f1e86ba0f4_dl.json&style=flat) | ![post_0f2ccf8471a3db168298f4f1e86ba0f4_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0f2ccf8471a3db168298f4f1e86ba0f4_vw.json&style=flat) | ![post_0f2ccf8471a3db168298f4f1e86ba0f4_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0f2ccf8471a3db168298f4f1e86ba0f4_up.json&style=flat) | ![post_0f2ccf8471a3db168298f4f1e86ba0f4_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0f2ccf8471a3db168298f4f1e86ba0f4_sv.json&style=flat) | 2026-02-28 |
| 21 | [🚀 GitHub Copilot SDK Pipe v0.7.0: Skills & Rich UI 🛠️](https://openwebui.com/posts/github_copilot_sdk_pipe_v070_native_tool_ui_zero_c_4af38131) | post | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_8aad0d77dbeaa0bdd3e6971274b5fa5c_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_8aad0d77dbeaa0bdd3e6971274b5fa5c_dl.json&style=flat) | ![post_8aad0d77dbeaa0bdd3e6971274b5fa5c_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_8aad0d77dbeaa0bdd3e6971274b5fa5c_vw.json&style=flat) | ![post_8aad0d77dbeaa0bdd3e6971274b5fa5c_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_8aad0d77dbeaa0bdd3e6971274b5fa5c_up.json&style=flat) | ![post_8aad0d77dbeaa0bdd3e6971274b5fa5c_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_8aad0d77dbeaa0bdd3e6971274b5fa5c_sv.json&style=flat) | 2026-02-28 |
| 22 | [🚀 GitHub Copilot SDK Pipe: AI That Executes, Not Just Talks](https://openwebui.com/posts/github_copilot_sdk_for_openwebui_elevate_your_ai_t_a140f293) | post | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_42a11f4f094a0fa757bfefece51b1180_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_42a11f4f094a0fa757bfefece51b1180_dl.json&style=flat) | ![post_42a11f4f094a0fa757bfefece51b1180_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_42a11f4f094a0fa757bfefece51b1180_vw.json&style=flat) | ![post_42a11f4f094a0fa757bfefece51b1180_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_42a11f4f094a0fa757bfefece51b1180_up.json&style=flat) | ![post_42a11f4f094a0fa757bfefece51b1180_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_42a11f4f094a0fa757bfefece51b1180_sv.json&style=flat) | 2026-02-10 |
| 23 | [🚀 Open WebUI Prompt Plus: AI-Powered Prompt Manager](https://openwebui.com/posts/open_webui_prompt_plus_ai_powered_prompt_manager_s_15fa060e) | post | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_858cb162732370288ce024b4f7944f69_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_858cb162732370288ce024b4f7944f69_dl.json&style=flat) | ![post_858cb162732370288ce024b4f7944f69_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_858cb162732370288ce024b4f7944f69_vw.json&style=flat) | ![post_858cb162732370288ce024b4f7944f69_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_858cb162732370288ce024b4f7944f69_up.json&style=flat) | ![post_858cb162732370288ce024b4f7944f69_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_858cb162732370288ce024b4f7944f69_sv.json&style=flat) | 2026-01-29 |
| 24 | [Review of Claude Haiku 4.5](https://openwebui.com/posts/review_of_claude_haiku_45_41b0db39) | review | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_8db923aec1f58d282a2334db388aa5c2_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_8db923aec1f58d282a2334db388aa5c2_dl.json&style=flat) | ![post_8db923aec1f58d282a2334db388aa5c2_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_8db923aec1f58d282a2334db388aa5c2_vw.json&style=flat) | ![post_8db923aec1f58d282a2334db388aa5c2_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_8db923aec1f58d282a2334db388aa5c2_up.json&style=flat) | ![post_8db923aec1f58d282a2334db388aa5c2_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_8db923aec1f58d282a2334db388aa5c2_sv.json&style=flat) | 2026-01-14 |
| 25 | [ 🛠️ Debug Open WebUI Plugins in Your Browser](https://openwebui.com/posts/debug_open_webui_plugins_in_your_browser_81bf7960) | post | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_e38b91e43262d29106345acfcd9dffba_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_e38b91e43262d29106345acfcd9dffba_dl.json&style=flat) | ![post_e38b91e43262d29106345acfcd9dffba_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_e38b91e43262d29106345acfcd9dffba_vw.json&style=flat) | ![post_e38b91e43262d29106345acfcd9dffba_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_e38b91e43262d29106345acfcd9dffba_up.json&style=flat) | ![post_e38b91e43262d29106345acfcd9dffba_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_e38b91e43262d29106345acfcd9dffba_sv.json&style=flat) | 2026-01-10 |
| 18 | [🧠 Smart Mind Map Tool: Auto-Generate Interactive Knowledge Graphs](https://openwebui.com/posts/smart_mind_map_tool_auto_generate_interactive_know_d25f4e3d) | tool | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_fdb1b2ff2b8c6ad0dbb3b15a0318434d_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_fdb1b2ff2b8c6ad0dbb3b15a0318434d_dl.json&style=flat) | ![post_fdb1b2ff2b8c6ad0dbb3b15a0318434d_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_fdb1b2ff2b8c6ad0dbb3b15a0318434d_vw.json&style=flat) | ![post_fdb1b2ff2b8c6ad0dbb3b15a0318434d_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_fdb1b2ff2b8c6ad0dbb3b15a0318434d_up.json&style=flat) | ![post_fdb1b2ff2b8c6ad0dbb3b15a0318434d_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_fdb1b2ff2b8c6ad0dbb3b15a0318434d_sv.json&style=flat) | 2026-03-05 |
| 19 | [闪记卡 (Flash Card)](https://openwebui.com/posts/闪记卡生成插件_4a31eac3) | action | ![v](https://img.shields.io/badge/v-0.2.4-blue?style=flat) | ![post_c68a2593e76ec324361358575b6501de_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c68a2593e76ec324361358575b6501de_dl.json&style=flat) | ![post_c68a2593e76ec324361358575b6501de_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c68a2593e76ec324361358575b6501de_vw.json&style=flat) | ![post_c68a2593e76ec324361358575b6501de_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c68a2593e76ec324361358575b6501de_up.json&style=flat) | ![post_c68a2593e76ec324361358575b6501de_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c68a2593e76ec324361358575b6501de_sv.json&style=flat) | 2026-02-13 |
| 20 | [精读](https://openwebui.com/posts/精读_99830b0f) | action | ![v](https://img.shields.io/badge/v-1.0.0-blue?style=flat) | ![post_14a3b01bc6559558b48a61a56dd3635d_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_14a3b01bc6559558b48a61a56dd3635d_dl.json&style=flat) | ![post_14a3b01bc6559558b48a61a56dd3635d_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_14a3b01bc6559558b48a61a56dd3635d_vw.json&style=flat) | ![post_14a3b01bc6559558b48a61a56dd3635d_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_14a3b01bc6559558b48a61a56dd3635d_up.json&style=flat) | ![post_14a3b01bc6559558b48a61a56dd3635d_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_14a3b01bc6559558b48a61a56dd3635d_sv.json&style=flat) | 2026-01-08 |
| 21 | [An Unconventional Use of Open Terminal ⚡](https://openwebui.com/posts/an_unconventional_use_of_open_terminal_35498f8f) | post | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_040794b40937926d42e6907044b828b1_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_040794b40937926d42e6907044b828b1_dl.json&style=flat) | ![post_040794b40937926d42e6907044b828b1_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_040794b40937926d42e6907044b828b1_vw.json&style=flat) | ![post_040794b40937926d42e6907044b828b1_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_040794b40937926d42e6907044b828b1_up.json&style=flat) | ![post_040794b40937926d42e6907044b828b1_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_040794b40937926d42e6907044b828b1_sv.json&style=flat) | 2026-03-06 |
| 22 | [🚀 GitHub Copilot SDK Pipe v0.9.0: Skills & RichUI](https://openwebui.com/posts/github_copilot_sdk_pipe_v090_copilot_sdk_skills_co_99a42452) | post | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_0f2ccf8471a3db168298f4f1e86ba0f4_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0f2ccf8471a3db168298f4f1e86ba0f4_dl.json&style=flat) | ![post_0f2ccf8471a3db168298f4f1e86ba0f4_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0f2ccf8471a3db168298f4f1e86ba0f4_vw.json&style=flat) | ![post_0f2ccf8471a3db168298f4f1e86ba0f4_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0f2ccf8471a3db168298f4f1e86ba0f4_up.json&style=flat) | ![post_0f2ccf8471a3db168298f4f1e86ba0f4_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0f2ccf8471a3db168298f4f1e86ba0f4_sv.json&style=flat) | 2026-02-28 |
| 23 | [🚀 GitHub Copilot SDK Pipe v0.7.0: Skills & Rich UI 🛠️](https://openwebui.com/posts/github_copilot_sdk_pipe_v070_native_tool_ui_zero_c_4af38131) | post | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_8aad0d77dbeaa0bdd3e6971274b5fa5c_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_8aad0d77dbeaa0bdd3e6971274b5fa5c_dl.json&style=flat) | ![post_8aad0d77dbeaa0bdd3e6971274b5fa5c_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_8aad0d77dbeaa0bdd3e6971274b5fa5c_vw.json&style=flat) | ![post_8aad0d77dbeaa0bdd3e6971274b5fa5c_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_8aad0d77dbeaa0bdd3e6971274b5fa5c_up.json&style=flat) | ![post_8aad0d77dbeaa0bdd3e6971274b5fa5c_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_8aad0d77dbeaa0bdd3e6971274b5fa5c_sv.json&style=flat) | 2026-02-28 |
| 24 | [🚀 GitHub Copilot SDK Pipe: AI That Executes, Not Just Talks](https://openwebui.com/posts/github_copilot_sdk_for_openwebui_elevate_your_ai_t_a140f293) | post | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_42a11f4f094a0fa757bfefece51b1180_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_42a11f4f094a0fa757bfefece51b1180_dl.json&style=flat) | ![post_42a11f4f094a0fa757bfefece51b1180_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_42a11f4f094a0fa757bfefece51b1180_vw.json&style=flat) | ![post_42a11f4f094a0fa757bfefece51b1180_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_42a11f4f094a0fa757bfefece51b1180_up.json&style=flat) | ![post_42a11f4f094a0fa757bfefece51b1180_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_42a11f4f094a0fa757bfefece51b1180_sv.json&style=flat) | 2026-02-10 |
| 25 | [🚀 Open WebUI Prompt Plus: AI-Powered Prompt Manager](https://openwebui.com/posts/open_webui_prompt_plus_ai_powered_prompt_manager_s_15fa060e) | post | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_858cb162732370288ce024b4f7944f69_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_858cb162732370288ce024b4f7944f69_dl.json&style=flat) | ![post_858cb162732370288ce024b4f7944f69_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_858cb162732370288ce024b4f7944f69_vw.json&style=flat) | ![post_858cb162732370288ce024b4f7944f69_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_858cb162732370288ce024b4f7944f69_up.json&style=flat) | ![post_858cb162732370288ce024b4f7944f69_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_858cb162732370288ce024b4f7944f69_sv.json&style=flat) | 2026-01-28 |
| 26 | [Review of Claude Haiku 4.5](https://openwebui.com/posts/review_of_claude_haiku_45_41b0db39) | review | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_8db923aec1f58d282a2334db388aa5c2_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_8db923aec1f58d282a2334db388aa5c2_dl.json&style=flat) | ![post_8db923aec1f58d282a2334db388aa5c2_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_8db923aec1f58d282a2334db388aa5c2_vw.json&style=flat) | ![post_8db923aec1f58d282a2334db388aa5c2_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_8db923aec1f58d282a2334db388aa5c2_up.json&style=flat) | ![post_8db923aec1f58d282a2334db388aa5c2_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_8db923aec1f58d282a2334db388aa5c2_sv.json&style=flat) | 2026-01-14 |
| 27 | [ 🛠️ Debug Open WebUI Plugins in Your Browser](https://openwebui.com/posts/debug_open_webui_plugins_in_your_browser_81bf7960) | post | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_e38b91e43262d29106345acfcd9dffba_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_e38b91e43262d29106345acfcd9dffba_dl.json&style=flat) | ![post_e38b91e43262d29106345acfcd9dffba_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_e38b91e43262d29106345acfcd9dffba_vw.json&style=flat) | ![post_e38b91e43262d29106345acfcd9dffba_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_e38b91e43262d29106345acfcd9dffba_up.json&style=flat) | ![post_e38b91e43262d29106345acfcd9dffba_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_e38b91e43262d29106345acfcd9dffba_sv.json&style=flat) | 2026-01-10 |

View File

@@ -8,7 +8,7 @@
> *蓝色: 总下载量 | 紫色: 总浏览量 (实时动态生成)*
### 📂 内容分类占比 (Distribution)
![Distribution](https://kroki.io/mermaid/svg/eNpNjEEOQEAMRfdO0cwNCBtrB7BwAaTkJ0ObmSJuz2Dh795r_1cwGcwzudZvM1bqTuVIDaIFDJtBVpfRHWci3lFN-Ysq0RJWH0L5d53gjUMS5Sv68ZlKH8XXCLKo_TqBd_DxiAuzSCeb)
![Distribution](https://kroki.io/mermaid/svg/eNoryExVKMksyUlVUArIKU3PzFMIqSxILVZwySwuKcpMKi3JzM9T4lIAAqWC_OISJQUrBTMItyQ_PwfENYLKZhakgriGEG5aZk5JahFIwAQikJgMNgqkAqajKD-3oARJT1FqWWZqOVgAALS1J50=)
## 📈 总览
@@ -25,8 +25,8 @@
## 📂 按类型分类
- ![tool](https://img.shields.io/badge/tool-1-teal)
- ![post](https://img.shields.io/badge/post-5-blue)
- ![post](https://img.shields.io/badge/post-6-blue)
- ![tool](https://img.shields.io/badge/tool-2-teal)
- ![pipe](https://img.shields.io/badge/pipe-1-blueviolet)
- ![filter](https://img.shields.io/badge/filter-4-brightgreen)
- ![action](https://img.shields.io/badge/action-12-orange)
@@ -37,28 +37,30 @@
| 排名 | 标题 | 类型 | 版本 | 下载 | 浏览 | 点赞 | 收藏 | 更新日期 |
|:---:|------|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| 1 | [Smart Mind Map](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a) | action | ![v](https://img.shields.io/badge/v-1.0.0-blue?style=flat) | ![post_207cd433d27fdd853ccbfa941e6fa67f_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_207cd433d27fdd853ccbfa941e6fa67f_dl.json&style=flat) | ![post_207cd433d27fdd853ccbfa941e6fa67f_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_207cd433d27fdd853ccbfa941e6fa67f_vw.json&style=flat) | ![post_207cd433d27fdd853ccbfa941e6fa67f_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_207cd433d27fdd853ccbfa941e6fa67f_up.json&style=flat) | ![post_207cd433d27fdd853ccbfa941e6fa67f_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_207cd433d27fdd853ccbfa941e6fa67f_sv.json&style=flat) | 2026-02-28 |
| 1 | [Smart Mind Map](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a) | action | ![v](https://img.shields.io/badge/v-1.0.0-blue?style=flat) | ![post_207cd433d27fdd853ccbfa941e6fa67f_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_207cd433d27fdd853ccbfa941e6fa67f_dl.json&style=flat) | ![post_207cd433d27fdd853ccbfa941e6fa67f_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_207cd433d27fdd853ccbfa941e6fa67f_vw.json&style=flat) | ![post_207cd433d27fdd853ccbfa941e6fa67f_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_207cd433d27fdd853ccbfa941e6fa67f_up.json&style=flat) | ![post_207cd433d27fdd853ccbfa941e6fa67f_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_207cd433d27fdd853ccbfa941e6fa67f_sv.json&style=flat) | 2026-02-27 |
| 2 | [Smart Infographic](https://openwebui.com/posts/smart_infographic_ad6f0c7f) | action | ![v](https://img.shields.io/badge/v-1.5.0-blue?style=flat) | ![post_376135b87514e63570283b2057459b1f_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_376135b87514e63570283b2057459b1f_dl.json&style=flat) | ![post_376135b87514e63570283b2057459b1f_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_376135b87514e63570283b2057459b1f_vw.json&style=flat) | ![post_376135b87514e63570283b2057459b1f_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_376135b87514e63570283b2057459b1f_up.json&style=flat) | ![post_376135b87514e63570283b2057459b1f_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_376135b87514e63570283b2057459b1f_sv.json&style=flat) | 2026-02-13 |
| 3 | [Markdown Normalizer](https://openwebui.com/posts/markdown_normalizer_baaa8732) | filter | ![v](https://img.shields.io/badge/v-1.2.7-blue?style=flat) | ![post_a963da7c5d914310e7026b8c8a6635b0_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_a963da7c5d914310e7026b8c8a6635b0_dl.json&style=flat) | ![post_a963da7c5d914310e7026b8c8a6635b0_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_a963da7c5d914310e7026b8c8a6635b0_vw.json&style=flat) | ![post_a963da7c5d914310e7026b8c8a6635b0_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_a963da7c5d914310e7026b8c8a6635b0_up.json&style=flat) | ![post_a963da7c5d914310e7026b8c8a6635b0_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_a963da7c5d914310e7026b8c8a6635b0_sv.json&style=flat) | 2026-02-28 |
| 3 | [Markdown Normalizer](https://openwebui.com/posts/markdown_normalizer_baaa8732) | filter | ![v](https://img.shields.io/badge/v-1.2.7-blue?style=flat) | ![post_a963da7c5d914310e7026b8c8a6635b0_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_a963da7c5d914310e7026b8c8a6635b0_dl.json&style=flat) | ![post_a963da7c5d914310e7026b8c8a6635b0_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_a963da7c5d914310e7026b8c8a6635b0_vw.json&style=flat) | ![post_a963da7c5d914310e7026b8c8a6635b0_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_a963da7c5d914310e7026b8c8a6635b0_up.json&style=flat) | ![post_a963da7c5d914310e7026b8c8a6635b0_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_a963da7c5d914310e7026b8c8a6635b0_sv.json&style=flat) | 2026-03-03 |
| 4 | [Export to Word Enhanced](https://openwebui.com/posts/export_to_word_enhanced_formatting_fca6a315) | action | ![v](https://img.shields.io/badge/v-0.4.4-blue?style=flat) | ![post_dc072f01690dc8293384153dc0231d05_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_dc072f01690dc8293384153dc0231d05_dl.json&style=flat) | ![post_dc072f01690dc8293384153dc0231d05_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_dc072f01690dc8293384153dc0231d05_vw.json&style=flat) | ![post_dc072f01690dc8293384153dc0231d05_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_dc072f01690dc8293384153dc0231d05_up.json&style=flat) | ![post_dc072f01690dc8293384153dc0231d05_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_dc072f01690dc8293384153dc0231d05_sv.json&style=flat) | 2026-02-13 |
| 5 | [Async Context Compression](https://openwebui.com/posts/async_context_compression_b1655bc8) | filter | ![v](https://img.shields.io/badge/v-1.3.0-blue?style=flat) | ![post_0640a7ef0970872217cb939f2ba9c12c_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0640a7ef0970872217cb939f2ba9c12c_dl.json&style=flat) | ![post_0640a7ef0970872217cb939f2ba9c12c_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0640a7ef0970872217cb939f2ba9c12c_vw.json&style=flat) | ![post_0640a7ef0970872217cb939f2ba9c12c_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0640a7ef0970872217cb939f2ba9c12c_up.json&style=flat) | ![post_0640a7ef0970872217cb939f2ba9c12c_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0640a7ef0970872217cb939f2ba9c12c_sv.json&style=flat) | 2026-02-28 |
| 6 | [Export to Excel](https://openwebui.com/posts/export_mulit_table_to_excel_244b8f9d) | action | ![v](https://img.shields.io/badge/v-0.3.7-blue?style=flat) | ![post_c5ae05d46c7b999a1e36ca1457f1926b_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c5ae05d46c7b999a1e36ca1457f1926b_dl.json&style=flat) | ![post_c5ae05d46c7b999a1e36ca1457f1926b_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c5ae05d46c7b999a1e36ca1457f1926b_vw.json&style=flat) | ![post_c5ae05d46c7b999a1e36ca1457f1926b_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c5ae05d46c7b999a1e36ca1457f1926b_up.json&style=flat) | ![post_c5ae05d46c7b999a1e36ca1457f1926b_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c5ae05d46c7b999a1e36ca1457f1926b_sv.json&style=flat) | 2026-02-13 |
| 7 | [AI Task Instruction Generator](https://openwebui.com/posts/ai_task_instruction_generator_9bab8b37) | prompt | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_19f469a02b32d21f86b16d1395a81317_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_19f469a02b32d21f86b16d1395a81317_dl.json&style=flat) | ![post_19f469a02b32d21f86b16d1395a81317_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_19f469a02b32d21f86b16d1395a81317_vw.json&style=flat) | ![post_19f469a02b32d21f86b16d1395a81317_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_19f469a02b32d21f86b16d1395a81317_up.json&style=flat) | ![post_19f469a02b32d21f86b16d1395a81317_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_19f469a02b32d21f86b16d1395a81317_sv.json&style=flat) | 2026-01-28 |
| 8 | [GitHub Copilot Official SDK Pipe](https://openwebui.com/posts/github_copilot_official_sdk_pipe_ce96f7b4) | pipe | ![v](https://img.shields.io/badge/v-0.9.0-blue?style=flat) | ![post_aef940e01073e811a311c3a443d9c149_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_aef940e01073e811a311c3a443d9c149_dl.json&style=flat) | ![post_aef940e01073e811a311c3a443d9c149_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_aef940e01073e811a311c3a443d9c149_vw.json&style=flat) | ![post_aef940e01073e811a311c3a443d9c149_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_aef940e01073e811a311c3a443d9c149_up.json&style=flat) | ![post_aef940e01073e811a311c3a443d9c149_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_aef940e01073e811a311c3a443d9c149_sv.json&style=flat) | 2026-02-28 |
| 5 | [Async Context Compression](https://openwebui.com/posts/async_context_compression_b1655bc8) | filter | ![v](https://img.shields.io/badge/v-1.3.0-blue?style=flat) | ![post_0640a7ef0970872217cb939f2ba9c12c_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0640a7ef0970872217cb939f2ba9c12c_dl.json&style=flat) | ![post_0640a7ef0970872217cb939f2ba9c12c_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0640a7ef0970872217cb939f2ba9c12c_vw.json&style=flat) | ![post_0640a7ef0970872217cb939f2ba9c12c_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0640a7ef0970872217cb939f2ba9c12c_up.json&style=flat) | ![post_0640a7ef0970872217cb939f2ba9c12c_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0640a7ef0970872217cb939f2ba9c12c_sv.json&style=flat) | 2026-03-03 |
| 6 | [AI Task Instruction Generator](https://openwebui.com/posts/ai_task_instruction_generator_9bab8b37) | prompt | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_19f469a02b32d21f86b16d1395a81317_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_19f469a02b32d21f86b16d1395a81317_dl.json&style=flat) | ![post_19f469a02b32d21f86b16d1395a81317_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_19f469a02b32d21f86b16d1395a81317_vw.json&style=flat) | ![post_19f469a02b32d21f86b16d1395a81317_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_19f469a02b32d21f86b16d1395a81317_up.json&style=flat) | ![post_19f469a02b32d21f86b16d1395a81317_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_19f469a02b32d21f86b16d1395a81317_sv.json&style=flat) | 2026-01-28 |
| 7 | [Export to Excel](https://openwebui.com/posts/export_mulit_table_to_excel_244b8f9d) | action | ![v](https://img.shields.io/badge/v-0.3.7-blue?style=flat) | ![post_c5ae05d46c7b999a1e36ca1457f1926b_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c5ae05d46c7b999a1e36ca1457f1926b_dl.json&style=flat) | ![post_c5ae05d46c7b999a1e36ca1457f1926b_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c5ae05d46c7b999a1e36ca1457f1926b_vw.json&style=flat) | ![post_c5ae05d46c7b999a1e36ca1457f1926b_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c5ae05d46c7b999a1e36ca1457f1926b_up.json&style=flat) | ![post_c5ae05d46c7b999a1e36ca1457f1926b_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c5ae05d46c7b999a1e36ca1457f1926b_sv.json&style=flat) | 2026-02-13 |
| 8 | [GitHub Copilot Official SDK Pipe](https://openwebui.com/posts/github_copilot_official_sdk_pipe_ce96f7b4) | pipe | ![v](https://img.shields.io/badge/v-0.9.1-blue?style=flat) | ![post_aef940e01073e811a311c3a443d9c149_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_aef940e01073e811a311c3a443d9c149_dl.json&style=flat) | ![post_aef940e01073e811a311c3a443d9c149_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_aef940e01073e811a311c3a443d9c149_vw.json&style=flat) | ![post_aef940e01073e811a311c3a443d9c149_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_aef940e01073e811a311c3a443d9c149_up.json&style=flat) | ![post_aef940e01073e811a311c3a443d9c149_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_aef940e01073e811a311c3a443d9c149_sv.json&style=flat) | 2026-03-03 |
| 9 | [Flash Card](https://openwebui.com/posts/flash_card_65a2ea8f) | action | ![v](https://img.shields.io/badge/v-0.2.4-blue?style=flat) | ![post_5cf7a5dad74ef15ee4ffc6fe10da8213_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_5cf7a5dad74ef15ee4ffc6fe10da8213_dl.json&style=flat) | ![post_5cf7a5dad74ef15ee4ffc6fe10da8213_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_5cf7a5dad74ef15ee4ffc6fe10da8213_vw.json&style=flat) | ![post_5cf7a5dad74ef15ee4ffc6fe10da8213_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_5cf7a5dad74ef15ee4ffc6fe10da8213_up.json&style=flat) | ![post_5cf7a5dad74ef15ee4ffc6fe10da8213_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_5cf7a5dad74ef15ee4ffc6fe10da8213_sv.json&style=flat) | 2026-02-13 |
| 10 | [Deep Dive](https://openwebui.com/posts/deep_dive_c0b846e4) | action | ![v](https://img.shields.io/badge/v-1.0.0-blue?style=flat) | ![post_78e3480de4e6bd4615498495c5d15979_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_78e3480de4e6bd4615498495c5d15979_dl.json&style=flat) | ![post_78e3480de4e6bd4615498495c5d15979_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_78e3480de4e6bd4615498495c5d15979_vw.json&style=flat) | ![post_78e3480de4e6bd4615498495c5d15979_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_78e3480de4e6bd4615498495c5d15979_up.json&style=flat) | ![post_78e3480de4e6bd4615498495c5d15979_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_78e3480de4e6bd4615498495c5d15979_sv.json&style=flat) | 2026-01-08 |
| 11 | [OpenWebUI Skills Manager Tool](https://openwebui.com/posts/openwebui_skills_manager_tool_b4bce8e4) | tool | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_6e9b698faed2ca2ba6f8308635a4804e_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_6e9b698faed2ca2ba6f8308635a4804e_dl.json&style=flat) | ![post_6e9b698faed2ca2ba6f8308635a4804e_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_6e9b698faed2ca2ba6f8308635a4804e_vw.json&style=flat) | ![post_6e9b698faed2ca2ba6f8308635a4804e_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_6e9b698faed2ca2ba6f8308635a4804e_up.json&style=flat) | ![post_6e9b698faed2ca2ba6f8308635a4804e_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_6e9b698faed2ca2ba6f8308635a4804e_sv.json&style=flat) | 2026-02-28 |
| 10 | [OpenWebUI Skills Manager Tool](https://openwebui.com/posts/openwebui_skills_manager_tool_b4bce8e4) | tool | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_6e9b698faed2ca2ba6f8308635a4804e_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_6e9b698faed2ca2ba6f8308635a4804e_dl.json&style=flat) | ![post_6e9b698faed2ca2ba6f8308635a4804e_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_6e9b698faed2ca2ba6f8308635a4804e_vw.json&style=flat) | ![post_6e9b698faed2ca2ba6f8308635a4804e_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_6e9b698faed2ca2ba6f8308635a4804e_up.json&style=flat) | ![post_6e9b698faed2ca2ba6f8308635a4804e_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_6e9b698faed2ca2ba6f8308635a4804e_sv.json&style=flat) | 2026-03-05 |
| 11 | [Deep Dive](https://openwebui.com/posts/deep_dive_c0b846e4) | action | ![v](https://img.shields.io/badge/v-1.0.0-blue?style=flat) | ![post_78e3480de4e6bd4615498495c5d15979_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_78e3480de4e6bd4615498495c5d15979_dl.json&style=flat) | ![post_78e3480de4e6bd4615498495c5d15979_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_78e3480de4e6bd4615498495c5d15979_vw.json&style=flat) | ![post_78e3480de4e6bd4615498495c5d15979_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_78e3480de4e6bd4615498495c5d15979_up.json&style=flat) | ![post_78e3480de4e6bd4615498495c5d15979_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_78e3480de4e6bd4615498495c5d15979_sv.json&style=flat) | 2026-01-08 |
| 12 | [导出为Word增强版](https://openwebui.com/posts/导出为_word_支持公式流程图表格和代码块_8a6306c0) | action | ![v](https://img.shields.io/badge/v-0.4.4-blue?style=flat) | ![post_fc4cbdf0eb78bd98c2ed7650d459b59e_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_fc4cbdf0eb78bd98c2ed7650d459b59e_dl.json&style=flat) | ![post_fc4cbdf0eb78bd98c2ed7650d459b59e_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_fc4cbdf0eb78bd98c2ed7650d459b59e_vw.json&style=flat) | ![post_fc4cbdf0eb78bd98c2ed7650d459b59e_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_fc4cbdf0eb78bd98c2ed7650d459b59e_up.json&style=flat) | ![post_fc4cbdf0eb78bd98c2ed7650d459b59e_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_fc4cbdf0eb78bd98c2ed7650d459b59e_sv.json&style=flat) | 2026-02-13 |
| 13 | [📂 Folder Memory Auto-Evolving Project Context](https://openwebui.com/posts/folder_memory_auto_evolving_project_context_4a9875b2) | filter | ![v](https://img.shields.io/badge/v-0.1.0-blue?style=flat) | ![post_a125b3a3702a07c86c77668dcede2149_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_a125b3a3702a07c86c77668dcede2149_dl.json&style=flat) | ![post_a125b3a3702a07c86c77668dcede2149_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_a125b3a3702a07c86c77668dcede2149_vw.json&style=flat) | ![post_a125b3a3702a07c86c77668dcede2149_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_a125b3a3702a07c86c77668dcede2149_up.json&style=flat) | ![post_a125b3a3702a07c86c77668dcede2149_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_a125b3a3702a07c86c77668dcede2149_sv.json&style=flat) | 2026-01-20 |
| 14 | [GitHub Copilot SDK Files Filter](https://openwebui.com/posts/github_copilot_sdk_files_filter_403a62ee) | filter | ![v](https://img.shields.io/badge/v-0.1.3-blue?style=flat) | ![post_68081377a06a5746efe798136a72c6b6_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_68081377a06a5746efe798136a72c6b6_dl.json&style=flat) | ![post_68081377a06a5746efe798136a72c6b6_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_68081377a06a5746efe798136a72c6b6_vw.json&style=flat) | ![post_68081377a06a5746efe798136a72c6b6_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_68081377a06a5746efe798136a72c6b6_up.json&style=flat) | ![post_68081377a06a5746efe798136a72c6b6_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_68081377a06a5746efe798136a72c6b6_sv.json&style=flat) | 2026-02-26 |
| 14 | [GitHub Copilot SDK Files Filter](https://openwebui.com/posts/github_copilot_sdk_files_filter_403a62ee) | filter | ![v](https://img.shields.io/badge/v-0.1.3-blue?style=flat) | ![post_68081377a06a5746efe798136a72c6b6_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_68081377a06a5746efe798136a72c6b6_dl.json&style=flat) | ![post_68081377a06a5746efe798136a72c6b6_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_68081377a06a5746efe798136a72c6b6_vw.json&style=flat) | ![post_68081377a06a5746efe798136a72c6b6_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_68081377a06a5746efe798136a72c6b6_up.json&style=flat) | ![post_68081377a06a5746efe798136a72c6b6_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_68081377a06a5746efe798136a72c6b6_sv.json&style=flat) | 2026-03-03 |
| 15 | [智能信息图](https://openwebui.com/posts/智能信息图_e04a48ff) | action | ![v](https://img.shields.io/badge/v-1.5.0-blue?style=flat) | ![post_0c3f8621f77a9d5875d3c26ee38a3954_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0c3f8621f77a9d5875d3c26ee38a3954_dl.json&style=flat) | ![post_0c3f8621f77a9d5875d3c26ee38a3954_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0c3f8621f77a9d5875d3c26ee38a3954_vw.json&style=flat) | ![post_0c3f8621f77a9d5875d3c26ee38a3954_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0c3f8621f77a9d5875d3c26ee38a3954_up.json&style=flat) | ![post_0c3f8621f77a9d5875d3c26ee38a3954_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0c3f8621f77a9d5875d3c26ee38a3954_sv.json&style=flat) | 2026-02-13 |
| 16 | [思维导图](https://openwebui.com/posts/智能生成交互式思维导图帮助用户可视化知识_8d4b097b) | action | ![v](https://img.shields.io/badge/v-0.9.2-blue?style=flat) | ![post_c5bfbfd0d29694a04c597e26202b824f_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c5bfbfd0d29694a04c597e26202b824f_dl.json&style=flat) | ![post_c5bfbfd0d29694a04c597e26202b824f_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c5bfbfd0d29694a04c597e26202b824f_vw.json&style=flat) | ![post_c5bfbfd0d29694a04c597e26202b824f_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c5bfbfd0d29694a04c597e26202b824f_up.json&style=flat) | ![post_c5bfbfd0d29694a04c597e26202b824f_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c5bfbfd0d29694a04c597e26202b824f_sv.json&style=flat) | 2026-02-13 |
| 17 | [异步上下文压缩](https://openwebui.com/posts/异步上下文压缩_5c0617cb) | action | ![v](https://img.shields.io/badge/v-1.2.2-blue?style=flat) | ![post_fa447e583483020bc1bf35e74302c75c_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_fa447e583483020bc1bf35e74302c75c_dl.json&style=flat) | ![post_fa447e583483020bc1bf35e74302c75c_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_fa447e583483020bc1bf35e74302c75c_vw.json&style=flat) | ![post_fa447e583483020bc1bf35e74302c75c_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_fa447e583483020bc1bf35e74302c75c_up.json&style=flat) | ![post_fa447e583483020bc1bf35e74302c75c_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_fa447e583483020bc1bf35e74302c75c_sv.json&style=flat) | 2026-02-13 |
| 18 | [闪记卡 (Flash Card)](https://openwebui.com/posts/闪记卡生成插件_4a31eac3) | action | ![v](https://img.shields.io/badge/v-0.2.4-blue?style=flat) | ![post_c68a2593e76ec324361358575b6501de_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c68a2593e76ec324361358575b6501de_dl.json&style=flat) | ![post_c68a2593e76ec324361358575b6501de_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c68a2593e76ec324361358575b6501de_vw.json&style=flat) | ![post_c68a2593e76ec324361358575b6501de_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c68a2593e76ec324361358575b6501de_up.json&style=flat) | ![post_c68a2593e76ec324361358575b6501de_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c68a2593e76ec324361358575b6501de_sv.json&style=flat) | 2026-02-13 |
| 19 | [精读](https://openwebui.com/posts/精读_99830b0f) | action | ![v](https://img.shields.io/badge/v-1.0.0-blue?style=flat) | ![post_14a3b01bc6559558b48a61a56dd3635d_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_14a3b01bc6559558b48a61a56dd3635d_dl.json&style=flat) | ![post_14a3b01bc6559558b48a61a56dd3635d_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_14a3b01bc6559558b48a61a56dd3635d_vw.json&style=flat) | ![post_14a3b01bc6559558b48a61a56dd3635d_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_14a3b01bc6559558b48a61a56dd3635d_up.json&style=flat) | ![post_14a3b01bc6559558b48a61a56dd3635d_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_14a3b01bc6559558b48a61a56dd3635d_sv.json&style=flat) | 2026-01-08 |
| 20 | [🚀 GitHub Copilot SDK Pipe v0.9.0: Skills & RichUI](https://openwebui.com/posts/github_copilot_sdk_pipe_v090_copilot_sdk_skills_co_99a42452) | post | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_0f2ccf8471a3db168298f4f1e86ba0f4_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0f2ccf8471a3db168298f4f1e86ba0f4_dl.json&style=flat) | ![post_0f2ccf8471a3db168298f4f1e86ba0f4_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0f2ccf8471a3db168298f4f1e86ba0f4_vw.json&style=flat) | ![post_0f2ccf8471a3db168298f4f1e86ba0f4_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0f2ccf8471a3db168298f4f1e86ba0f4_up.json&style=flat) | ![post_0f2ccf8471a3db168298f4f1e86ba0f4_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0f2ccf8471a3db168298f4f1e86ba0f4_sv.json&style=flat) | 2026-02-28 |
| 21 | [🚀 GitHub Copilot SDK Pipe v0.7.0: Skills & Rich UI 🛠️](https://openwebui.com/posts/github_copilot_sdk_pipe_v070_native_tool_ui_zero_c_4af38131) | post | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_8aad0d77dbeaa0bdd3e6971274b5fa5c_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_8aad0d77dbeaa0bdd3e6971274b5fa5c_dl.json&style=flat) | ![post_8aad0d77dbeaa0bdd3e6971274b5fa5c_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_8aad0d77dbeaa0bdd3e6971274b5fa5c_vw.json&style=flat) | ![post_8aad0d77dbeaa0bdd3e6971274b5fa5c_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_8aad0d77dbeaa0bdd3e6971274b5fa5c_up.json&style=flat) | ![post_8aad0d77dbeaa0bdd3e6971274b5fa5c_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_8aad0d77dbeaa0bdd3e6971274b5fa5c_sv.json&style=flat) | 2026-02-28 |
| 22 | [🚀 GitHub Copilot SDK Pipe: AI That Executes, Not Just Talks](https://openwebui.com/posts/github_copilot_sdk_for_openwebui_elevate_your_ai_t_a140f293) | post | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_42a11f4f094a0fa757bfefece51b1180_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_42a11f4f094a0fa757bfefece51b1180_dl.json&style=flat) | ![post_42a11f4f094a0fa757bfefece51b1180_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_42a11f4f094a0fa757bfefece51b1180_vw.json&style=flat) | ![post_42a11f4f094a0fa757bfefece51b1180_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_42a11f4f094a0fa757bfefece51b1180_up.json&style=flat) | ![post_42a11f4f094a0fa757bfefece51b1180_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_42a11f4f094a0fa757bfefece51b1180_sv.json&style=flat) | 2026-02-10 |
| 23 | [🚀 Open WebUI Prompt Plus: AI-Powered Prompt Manager](https://openwebui.com/posts/open_webui_prompt_plus_ai_powered_prompt_manager_s_15fa060e) | post | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_858cb162732370288ce024b4f7944f69_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_858cb162732370288ce024b4f7944f69_dl.json&style=flat) | ![post_858cb162732370288ce024b4f7944f69_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_858cb162732370288ce024b4f7944f69_vw.json&style=flat) | ![post_858cb162732370288ce024b4f7944f69_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_858cb162732370288ce024b4f7944f69_up.json&style=flat) | ![post_858cb162732370288ce024b4f7944f69_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_858cb162732370288ce024b4f7944f69_sv.json&style=flat) | 2026-01-29 |
| 24 | [Review of Claude Haiku 4.5](https://openwebui.com/posts/review_of_claude_haiku_45_41b0db39) | review | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_8db923aec1f58d282a2334db388aa5c2_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_8db923aec1f58d282a2334db388aa5c2_dl.json&style=flat) | ![post_8db923aec1f58d282a2334db388aa5c2_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_8db923aec1f58d282a2334db388aa5c2_vw.json&style=flat) | ![post_8db923aec1f58d282a2334db388aa5c2_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_8db923aec1f58d282a2334db388aa5c2_up.json&style=flat) | ![post_8db923aec1f58d282a2334db388aa5c2_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_8db923aec1f58d282a2334db388aa5c2_sv.json&style=flat) | 2026-01-14 |
| 25 | [ 🛠️ Debug Open WebUI Plugins in Your Browser](https://openwebui.com/posts/debug_open_webui_plugins_in_your_browser_81bf7960) | post | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_e38b91e43262d29106345acfcd9dffba_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_e38b91e43262d29106345acfcd9dffba_dl.json&style=flat) | ![post_e38b91e43262d29106345acfcd9dffba_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_e38b91e43262d29106345acfcd9dffba_vw.json&style=flat) | ![post_e38b91e43262d29106345acfcd9dffba_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_e38b91e43262d29106345acfcd9dffba_up.json&style=flat) | ![post_e38b91e43262d29106345acfcd9dffba_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_e38b91e43262d29106345acfcd9dffba_sv.json&style=flat) | 2026-01-10 |
| 18 | [🧠 Smart Mind Map Tool: Auto-Generate Interactive Knowledge Graphs](https://openwebui.com/posts/smart_mind_map_tool_auto_generate_interactive_know_d25f4e3d) | tool | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_fdb1b2ff2b8c6ad0dbb3b15a0318434d_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_fdb1b2ff2b8c6ad0dbb3b15a0318434d_dl.json&style=flat) | ![post_fdb1b2ff2b8c6ad0dbb3b15a0318434d_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_fdb1b2ff2b8c6ad0dbb3b15a0318434d_vw.json&style=flat) | ![post_fdb1b2ff2b8c6ad0dbb3b15a0318434d_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_fdb1b2ff2b8c6ad0dbb3b15a0318434d_up.json&style=flat) | ![post_fdb1b2ff2b8c6ad0dbb3b15a0318434d_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_fdb1b2ff2b8c6ad0dbb3b15a0318434d_sv.json&style=flat) | 2026-03-05 |
| 19 | [闪记卡 (Flash Card)](https://openwebui.com/posts/闪记卡生成插件_4a31eac3) | action | ![v](https://img.shields.io/badge/v-0.2.4-blue?style=flat) | ![post_c68a2593e76ec324361358575b6501de_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c68a2593e76ec324361358575b6501de_dl.json&style=flat) | ![post_c68a2593e76ec324361358575b6501de_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c68a2593e76ec324361358575b6501de_vw.json&style=flat) | ![post_c68a2593e76ec324361358575b6501de_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c68a2593e76ec324361358575b6501de_up.json&style=flat) | ![post_c68a2593e76ec324361358575b6501de_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_c68a2593e76ec324361358575b6501de_sv.json&style=flat) | 2026-02-13 |
| 20 | [精读](https://openwebui.com/posts/精读_99830b0f) | action | ![v](https://img.shields.io/badge/v-1.0.0-blue?style=flat) | ![post_14a3b01bc6559558b48a61a56dd3635d_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_14a3b01bc6559558b48a61a56dd3635d_dl.json&style=flat) | ![post_14a3b01bc6559558b48a61a56dd3635d_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_14a3b01bc6559558b48a61a56dd3635d_vw.json&style=flat) | ![post_14a3b01bc6559558b48a61a56dd3635d_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_14a3b01bc6559558b48a61a56dd3635d_up.json&style=flat) | ![post_14a3b01bc6559558b48a61a56dd3635d_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_14a3b01bc6559558b48a61a56dd3635d_sv.json&style=flat) | 2026-01-08 |
| 21 | [An Unconventional Use of Open Terminal ⚡](https://openwebui.com/posts/an_unconventional_use_of_open_terminal_35498f8f) | post | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_040794b40937926d42e6907044b828b1_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_040794b40937926d42e6907044b828b1_dl.json&style=flat) | ![post_040794b40937926d42e6907044b828b1_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_040794b40937926d42e6907044b828b1_vw.json&style=flat) | ![post_040794b40937926d42e6907044b828b1_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_040794b40937926d42e6907044b828b1_up.json&style=flat) | ![post_040794b40937926d42e6907044b828b1_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_040794b40937926d42e6907044b828b1_sv.json&style=flat) | 2026-03-06 |
| 22 | [🚀 GitHub Copilot SDK Pipe v0.9.0: Skills & RichUI](https://openwebui.com/posts/github_copilot_sdk_pipe_v090_copilot_sdk_skills_co_99a42452) | post | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_0f2ccf8471a3db168298f4f1e86ba0f4_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0f2ccf8471a3db168298f4f1e86ba0f4_dl.json&style=flat) | ![post_0f2ccf8471a3db168298f4f1e86ba0f4_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0f2ccf8471a3db168298f4f1e86ba0f4_vw.json&style=flat) | ![post_0f2ccf8471a3db168298f4f1e86ba0f4_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0f2ccf8471a3db168298f4f1e86ba0f4_up.json&style=flat) | ![post_0f2ccf8471a3db168298f4f1e86ba0f4_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_0f2ccf8471a3db168298f4f1e86ba0f4_sv.json&style=flat) | 2026-02-28 |
| 23 | [🚀 GitHub Copilot SDK Pipe v0.7.0: Skills & Rich UI 🛠️](https://openwebui.com/posts/github_copilot_sdk_pipe_v070_native_tool_ui_zero_c_4af38131) | post | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_8aad0d77dbeaa0bdd3e6971274b5fa5c_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_8aad0d77dbeaa0bdd3e6971274b5fa5c_dl.json&style=flat) | ![post_8aad0d77dbeaa0bdd3e6971274b5fa5c_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_8aad0d77dbeaa0bdd3e6971274b5fa5c_vw.json&style=flat) | ![post_8aad0d77dbeaa0bdd3e6971274b5fa5c_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_8aad0d77dbeaa0bdd3e6971274b5fa5c_up.json&style=flat) | ![post_8aad0d77dbeaa0bdd3e6971274b5fa5c_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_8aad0d77dbeaa0bdd3e6971274b5fa5c_sv.json&style=flat) | 2026-02-28 |
| 24 | [🚀 GitHub Copilot SDK Pipe: AI That Executes, Not Just Talks](https://openwebui.com/posts/github_copilot_sdk_for_openwebui_elevate_your_ai_t_a140f293) | post | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_42a11f4f094a0fa757bfefece51b1180_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_42a11f4f094a0fa757bfefece51b1180_dl.json&style=flat) | ![post_42a11f4f094a0fa757bfefece51b1180_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_42a11f4f094a0fa757bfefece51b1180_vw.json&style=flat) | ![post_42a11f4f094a0fa757bfefece51b1180_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_42a11f4f094a0fa757bfefece51b1180_up.json&style=flat) | ![post_42a11f4f094a0fa757bfefece51b1180_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_42a11f4f094a0fa757bfefece51b1180_sv.json&style=flat) | 2026-02-10 |
| 25 | [🚀 Open WebUI Prompt Plus: AI-Powered Prompt Manager](https://openwebui.com/posts/open_webui_prompt_plus_ai_powered_prompt_manager_s_15fa060e) | post | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_858cb162732370288ce024b4f7944f69_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_858cb162732370288ce024b4f7944f69_dl.json&style=flat) | ![post_858cb162732370288ce024b4f7944f69_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_858cb162732370288ce024b4f7944f69_vw.json&style=flat) | ![post_858cb162732370288ce024b4f7944f69_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_858cb162732370288ce024b4f7944f69_up.json&style=flat) | ![post_858cb162732370288ce024b4f7944f69_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_858cb162732370288ce024b4f7944f69_sv.json&style=flat) | 2026-01-28 |
| 26 | [Review of Claude Haiku 4.5](https://openwebui.com/posts/review_of_claude_haiku_45_41b0db39) | review | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_8db923aec1f58d282a2334db388aa5c2_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_8db923aec1f58d282a2334db388aa5c2_dl.json&style=flat) | ![post_8db923aec1f58d282a2334db388aa5c2_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_8db923aec1f58d282a2334db388aa5c2_vw.json&style=flat) | ![post_8db923aec1f58d282a2334db388aa5c2_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_8db923aec1f58d282a2334db388aa5c2_up.json&style=flat) | ![post_8db923aec1f58d282a2334db388aa5c2_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_8db923aec1f58d282a2334db388aa5c2_sv.json&style=flat) | 2026-01-14 |
| 27 | [ 🛠️ Debug Open WebUI Plugins in Your Browser](https://openwebui.com/posts/debug_open_webui_plugins_in_your_browser_81bf7960) | post | ![v](https://img.shields.io/badge/v-N/A-gray?style=flat) | ![post_e38b91e43262d29106345acfcd9dffba_dl](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_e38b91e43262d29106345acfcd9dffba_dl.json&style=flat) | ![post_e38b91e43262d29106345acfcd9dffba_vw](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_e38b91e43262d29106345acfcd9dffba_vw.json&style=flat) | ![post_e38b91e43262d29106345acfcd9dffba_up](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_e38b91e43262d29106345acfcd9dffba_up.json&style=flat) | ![post_e38b91e43262d29106345acfcd9dffba_sv](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_post_e38b91e43262d29106345acfcd9dffba_sv.json&style=flat) | 2026-01-10 |

View File

@@ -0,0 +1,124 @@
# Fix: OpenAI API Error "messages with role 'tool' must be a response to a preceding message with 'tool_calls'"
## Problem Description
In the `async-context-compression` filter, chat history can be trimmed or summarized when the conversation grows. If the retained tail starts in the middle of a native tool-calling sequence, the next request may begin with a `tool` message whose triggering `assistant` message is no longer present.
That produces the OpenAI API error:
`"messages with role 'tool' must be a response to a preceding message with 'tool_calls'"`
## Root Cause
History compression boundaries were not fully aware of atomic tool-call chains. A valid chain may include:
1. An `assistant` message with `tool_calls`
2. One or more `tool` messages
3. An optional assistant follow-up that consumes the tool results
If truncation happens inside that chain, the request sent to the model becomes invalid.
## Solution: Atomic Boundary Alignment
The fix groups tool-call sequences into atomic units and aligns trim boundaries to those groups.
### 1. `_get_atomic_groups()`
This helper groups message indices into units that must be kept or dropped together. It explicitly recognizes native tool-calling patterns such as:
- `assistant(tool_calls)`
- `tool`
- assistant follow-up response
Conceptually, it treats the whole sequence as one atomic block instead of independent messages.
```python
def _get_atomic_groups(self, messages: List[Dict]) -> List[List[int]]:
groups = []
current_group = []
for i, msg in enumerate(messages):
role = msg.get("role")
has_tool_calls = bool(msg.get("tool_calls"))
if role == "assistant" and has_tool_calls:
if current_group:
groups.append(current_group)
current_group = [i]
elif role == "tool":
if not current_group:
groups.append([i])
else:
current_group.append(i)
elif (
role == "assistant"
and current_group
and messages[current_group[-1]].get("role") == "tool"
):
current_group.append(i)
groups.append(current_group)
current_group = []
else:
if current_group:
groups.append(current_group)
current_group = []
groups.append([i])
if current_group:
groups.append(current_group)
return groups
```
### 2. `_align_tail_start_to_atomic_boundary()`
This helper checks whether a proposed trim point falls inside one of those atomic groups. If it does, the start index is moved backward to the beginning of that group.
```python
def _align_tail_start_to_atomic_boundary(
self, messages: List[Dict], raw_start_index: int, protected_prefix: int
) -> int:
aligned_start = max(raw_start_index, protected_prefix)
if aligned_start <= protected_prefix or aligned_start >= len(messages):
return aligned_start
trimmable = messages[protected_prefix:]
local_start = aligned_start - protected_prefix
for group in self._get_atomic_groups(trimmable):
group_start = group[0]
group_end = group[-1] + 1
if local_start == group_start:
return aligned_start
if group_start < local_start < group_end:
return protected_prefix + group_start
return aligned_start
```
### 3. Applied to Tail Retention and Summary Progress
The aligned boundary is now used when rebuilding the retained tail and when calculating how much history can be summarized safely.
Example from the current implementation:
```python
raw_start_index = max(compressed_count, effective_keep_first)
start_index = self._align_tail_start_to_atomic_boundary(
messages, raw_start_index, effective_keep_first
)
tail_messages = messages[start_index:]
```
And during summary progress calculation:
```python
raw_target_compressed_count = max(0, len(messages) - self.valves.keep_last)
target_compressed_count = self._align_tail_start_to_atomic_boundary(
messages, raw_target_compressed_count, effective_keep_first
)
```
## Verification Results
- **First compression boundary**: When history first crosses the compression threshold, the retained tail no longer starts inside a tool-call block.
- **Complex sessions**: Real-world testing with 30+ messages, multiple tool calls, and failed calls remained stable during background summarization.
- **Regression behavior**: The filter now prefers a valid boundary even if that means retaining slightly more context than a naive raw slice would allow.
## Conclusion
The fix prevents orphaned `tool` messages by making history trimming and summary progress aware of atomic tool-call groups. This eliminates the 400 error during long conversations and background compression.

View File

@@ -0,0 +1,126 @@
# 修复OpenAI API 错误 "messages with role 'tool' must be a response to a preceding message with 'tool_calls'"
## 问题描述
`async-context-compression` 过滤器中,当对话历史变长时,系统会对消息进行裁剪或摘要。如果保留下来的尾部历史恰好从一个原生工具调用序列的中间开始,那么下一次请求就可能以一条 `tool` 消息开头,而触发它的 `assistant` 消息已经被裁掉。
这就会触发 OpenAI API 的错误:
`"messages with role 'tool' must be a response to a preceding message with 'tool_calls'"`
## 根本原因
真正的缺陷在于历史压缩边界没有完整识别工具调用链的“原子性”。一个合法的工具调用链通常包括:
1. 一条带有 `tool_calls``assistant` 消息
2. 一条或多条 `tool` 消息
3. 一条可选的 assistant 跟进回复,用于消费工具结果
如果裁剪点落在这段链条内部,发给模型的消息序列就会变成非法格式。
## 解决方案:对齐原子边界
修复通过把工具调用序列分组为原子单元,并使裁剪边界对齐到这些单元。
### 1. `_get_atomic_groups()`
这个辅助函数会把消息索引分组为“必须一起保留或一起丢弃”的原子单元。它显式识别以下原生工具调用模式:
- `assistant(tool_calls)`
- `tool`
- assistant 跟进回复
也就是说,它不再把这些消息看成彼此独立的单条消息,而是把整段序列视为一个原子块。
```python
def _get_atomic_groups(self, messages: List[Dict]) -> List[List[int]]:
groups = []
current_group = []
for i, msg in enumerate(messages):
role = msg.get("role")
has_tool_calls = bool(msg.get("tool_calls"))
if role == "assistant" and has_tool_calls:
if current_group:
groups.append(current_group)
current_group = [i]
elif role == "tool":
if not current_group:
groups.append([i])
else:
current_group.append(i)
elif (
role == "assistant"
and current_group
and messages[current_group[-1]].get("role") == "tool"
):
current_group.append(i)
groups.append(current_group)
current_group = []
else:
if current_group:
groups.append(current_group)
current_group = []
groups.append([i])
if current_group:
groups.append(current_group)
return groups
```
### 2. `_align_tail_start_to_atomic_boundary()`
这个辅助函数会检查一个拟定的裁剪起点是否落在某个原子块内部。如果是,它会把起点向前回退到该原子块的开头位置。
```python
def _align_tail_start_to_atomic_boundary(
self, messages: List[Dict], raw_start_index: int, protected_prefix: int
) -> int:
aligned_start = max(raw_start_index, protected_prefix)
if aligned_start <= protected_prefix or aligned_start >= len(messages):
return aligned_start
trimmable = messages[protected_prefix:]
local_start = aligned_start - protected_prefix
for group in self._get_atomic_groups(trimmable):
group_start = group[0]
group_end = group[-1] + 1
if local_start == group_start:
return aligned_start
if group_start < local_start < group_end:
return protected_prefix + group_start
return aligned_start
```
### 3. 应用于尾部保留和摘要进度计算
这个对齐后的边界现在被用于重建保留尾部消息,以及计算可以安全摘要的历史范围。
当前实现中的示例:
```python
raw_start_index = max(compressed_count, effective_keep_first)
start_index = self._align_tail_start_to_atomic_boundary(
messages, raw_start_index, effective_keep_first
)
tail_messages = messages[start_index:]
```
在摘要进度计算中同样如此:
```python
raw_target_compressed_count = max(0, len(messages) - self.valves.keep_last)
target_compressed_count = self._align_tail_start_to_atomic_boundary(
messages, raw_target_compressed_count, effective_keep_first
)
```
## 验证结果
- **首次压缩边界**:当历史第一次越过压缩阈值时,保留尾部不再从工具调用块中间开始。
- **复杂会话验证**:在 30+ 条消息、多个工具调用和失败调用的真实场景下,后台摘要过程保持稳定。
- **回归行为更安全**:过滤器现在会优先选择合法边界,即使这意味着比原始的朴素切片稍微多保留一点上下文。
## 结论
通过让历史裁剪与摘要进度计算具备"工具调用原子块感知"能力,避免孤立的 `tool` 消息出现,消除长对话与后台压缩期间的 400 错误。

View File

@@ -0,0 +1,426 @@
# gh-aw Integration Plan
> This document proposes a safe, incremental adoption plan for GitHub Agentic Workflows (`gh-aw`) in the `openwebui-extensions` repository.
---
## 1. Goals
- Add repository-aware AI maintenance without replacing stable script-based CI.
- Use `gh-aw` where natural language reasoning is stronger than deterministic shell logic.
- Preserve the current release, deploy, publish, and stats workflows as the execution backbone.
- Introduce observability, diagnosis, and long-term maintenance memory for repository operations.
---
## 2. Why gh-aw Fits This Repository
This repository already has strong deterministic automation:
- `/.github/workflows/release.yml`
- `/.github/workflows/plugin-version-check.yml`
- `/.github/workflows/deploy.yml`
- `/.github/workflows/publish_plugin.yml`
- `/.github/workflows/community-stats.yml`
Those workflows are good at exact execution, but they do not deeply understand repository policy.
`gh-aw` is a good fit for tasks that require:
- reading code, docs, and PR descriptions together
- applying repository conventions with nuance
- generating structured review comments
- diagnosing failed workflow runs
- keeping long-term maintenance notes across runs
This matches the repository's real needs:
- bilingual documentation synchronization
- plugin code + README + docs consistency
- release-prep validation across many files
- issue and PR maintenance at scale
---
## 3. Non-Goals
The first adoption phase should not:
- replace `release.yml`
- replace `publish_plugin.yml`
- replace MkDocs deployment
- auto-merge or auto-push code changes by default
- grant broad write permissions to the agent
`gh-aw` should begin as a review, diagnosis, and preflight layer.
---
## 4. Adoption Principles
### 4.1 Keep deterministic workflows for execution
Existing YAML workflows remain responsible for:
- release creation
- plugin publishing
- documentation deployment
- version extraction and comparison
- stats generation
### 4.2 Add agentic workflows for judgment
`gh-aw` workflows should focus on:
- policy-aware review
- release readiness checks
- docs drift analysis
- CI failure investigation
- issue triage and response drafting
### 4.3 Default to read-only behavior
Start with minimal permissions and use safe outputs only for controlled comments or issue creation.
### 4.4 Keep the blast radius small
Roll out one workflow at a time, verify output quality, then expand.
---
## 5. Proposed Repository Layout
### 5.1 New files and directories
```text
.github/
├── workflows/
│ ├── release.yml
│ ├── plugin-version-check.yml
│ ├── deploy.yml
│ ├── publish_plugin.yml
│ ├── community-stats.yml
│ ├── aw-pr-maintainer-review.md
│ ├── aw-pr-maintainer-review.lock.yml
│ ├── aw-release-preflight.md
│ ├── aw-release-preflight.lock.yml
│ ├── aw-ci-audit.md
│ ├── aw-ci-audit.lock.yml
│ ├── aw-docs-drift-review.md
│ └── aw-docs-drift-review.lock.yml
├── gh-aw/
│ ├── prompts/
│ │ ├── pr-review-policy.md
│ │ ├── release-preflight-policy.md
│ │ ├── ci-audit-policy.md
│ │ └── docs-drift-policy.md
│ ├── schemas/
│ │ └── review-output-example.json
│ └── README.md
└── copilot-instructions.md
```
### 5.2 Naming convention
Use an `aw-` prefix for all agentic workflow source files:
- `aw-pr-maintainer-review.md`
- `aw-release-preflight.md`
- `aw-ci-audit.md`
- `aw-docs-drift-review.md`
Reasons:
- clearly separates agentic workflows from existing handwritten YAML workflows
- keeps `gh-aw` assets easy to search
- avoids ambiguity during debugging and release review
### 5.3 Why not replace `.yml` files
The current workflows are production logic. `gh-aw` should complement them first, not absorb their responsibility.
---
## 6. Recommended Workflow Portfolio
### 6.1 Phase 1: PR Maintainer Review
**File**: `/.github/workflows/aw-pr-maintainer-review.md`
**Purpose**:
- review PRs that touch plugins, docs, or development guidance
- comment on missing repository-standard updates
- act as a semantic layer on top of `plugin-version-check.yml`
**Checks to perform**:
- plugin version updated when code changes
- `README.md` and `README_CN.md` both updated when required
- docs mirror pages updated when required
- root README badge/date update needed for release-related changes
- i18n and helper-method standards followed for plugin code
- Conventional Commit quality in PR title/body if relevant
**Suggested permissions**:
```yaml
permissions:
contents: read
pull-requests: write
issues: write
```
**Suggested tools**:
- `github:` read-focused issue/PR/repo tools
- `bash:` limited read commands only
- `edit:` disabled in early phase
- `agentic-workflows:` optional only after adoption matures
### 6.2 Phase 1: Release Preflight
**File**: `/.github/workflows/aw-release-preflight.md`
**Purpose**:
- run before release or on manual dispatch
- verify release completeness before `release.yml` does packaging and publishing
**Checks to perform**:
- code version and docs versions are aligned
- bilingual README updates exist
- docs plugin mirrors exist and match the release target
- release notes sources exist where expected
- commit message and release draft are coherent
**Output style**:
- summary comment on PR or issue
- optional checklist artifact
- no direct release creation
### 6.3 Phase 2: CI Audit
**File**: `/.github/workflows/aw-ci-audit.md`
**Purpose**:
- inspect failed runs of `release.yml`, `publish_plugin.yml`, `community-stats.yml`, and other important workflows
- summarize likely root cause and next fix steps
**Why gh-aw is strong here**:
- it can use `logs` and `audit` via `gh aw mcp-server`
- it is designed for workflow introspection and post-hoc analysis
### 6.4 Phase 2: Docs Drift Review
**File**: `/.github/workflows/aw-docs-drift-review.md`
**Purpose**:
- periodically inspect whether plugin code, local README files, mirrored docs, and root indexes have drifted apart
**Checks to perform**:
- missing `README_CN.md`
- README sections out of order
- docs page missing after plugin update
- version mismatches across code and docs
### 6.5 Phase 3: Issue Maintainer
**Candidate file**: `/.github/workflows/aw-issue-maintainer.md`
**Purpose**:
- summarize unreplied issues
- propose bilingual responses
- group repeated bug reports by plugin
This should come after the earlier review and audit flows are trusted.
---
## 7. Mapping to Existing Workflows
| Current Workflow | Keep As-Is | gh-aw Companion | Role Split |
|------|------|------|------|
| `/.github/workflows/release.yml` | Yes | `aw-release-preflight.md` | `release.yml` executes; `gh-aw` judges readiness |
| `/.github/workflows/plugin-version-check.yml` | Yes | `aw-pr-maintainer-review.md` | hard gate + semantic review |
| `/.github/workflows/deploy.yml` | Yes | none initially | deterministic build and deploy |
| `/.github/workflows/publish_plugin.yml` | Yes | `aw-ci-audit.md` | deterministic publish + failure diagnosis |
| `/.github/workflows/community-stats.yml` | Yes | `aw-ci-audit.md` | deterministic stats + anomaly diagnosis |
---
## 8. Tooling Model
### 8.1 Built-in tools to enable first
For early workflows, prefer a narrow tool set:
```yaml
tools:
github:
toolsets: [default]
bash:
- echo
- pwd
- ls
- cat
- head
- tail
- grep
- wc
- git status
- git diff
```
Do not enable unrestricted shell access in phase 1.
### 8.2 MCP usage model
Use `gh aw mcp-server` later for:
- workflow `status`
- workflow `compile`
- workflow `logs`
- workflow `audit`
- `mcp-inspect`
This is especially valuable for `aw-ci-audit.md`.
### 8.3 Safe output policy
In early adoption, only allow safe outputs that:
- comment on PRs
- comment on issues
- open a low-risk maintenance issue when explicitly needed
Avoid any automatic code-writing safe outputs at first.
---
## 9. Repo Memory Strategy
`gh-aw` repo memory is a strong fit for this repository, but it should be constrained.
### 9.1 Recommended first use cases
- recurring CI failure signatures
- repeated docs sync omissions
- common reviewer reminders
- issue clusters by plugin name
### 9.2 Recommended configuration shape
- store only `.md` and `.json`
- small patch size limit
- one memory stream per concern
Suggested conceptual layout:
```text
memory/review-notes/*.md
memory/ci-patterns/*.md
memory/issue-clusters/*.json
```
### 9.3 Important caution
Do not store secrets, tokens, or unpublished sensitive data in repo memory.
---
## 10. Rollout Plan
### Phase 0: Preparation
- install `gh-aw` locally for maintainers
- add a short `/.github/gh-aw/README.md`
- document workflow naming and review expectations
### Phase 1: Read-only semantic review
- introduce `aw-pr-maintainer-review.md`
- introduce `aw-release-preflight.md`
- keep outputs limited to summaries and comments
### Phase 2: Diagnostics and memory
- introduce `aw-ci-audit.md`
- enable `agentic-workflows:` where useful
- add constrained `repo-memory` configuration for repeated failure patterns
### Phase 3: Maintenance automation
- add docs drift patrol
- add issue maintenance workflow
- consider limited code-change proposals only after trust is established
---
## 11. Local Maintainer Setup
For local experimentation and debugging:
### 11.1 Install CLI
```bash
curl -sL https://raw.githubusercontent.com/github/gh-aw/main/install-gh-aw.sh | bash
```
### 11.2 Useful commands
```bash
gh aw version
gh aw compile
gh aw status
gh aw run aw-pr-maintainer-review
gh aw logs
gh aw audit <run-id>
```
### 11.3 VS Code MCP integration
A future optional improvement is adding `gh aw mcp-server` to local MCP configuration so workflow introspection tools are available in editor-based agent sessions.
---
## 12. Recommended First Deliverables
Start with these two workflows only:
1. `aw-pr-maintainer-review.md`
2. `aw-release-preflight.md`
This gives the repository the highest-value upgrade with the lowest operational risk.
---
## 13. Success Criteria
Adoption is working if:
- PR review comments become more specific and repository-aware
- release preparation catches missing docs or version sync earlier
- CI failures produce actionable summaries faster
- maintainers spend less time on repetitive policy review
- deterministic workflows remain stable and unchanged in core behavior
---
## 14. Summary
For `openwebui-extensions`, `gh-aw` should be adopted as an intelligent maintenance layer.
- Keep current YAML workflows for execution.
- Add agentic workflows for policy-aware review and diagnosis.
- Start read-only.
- Expand only after signal quality is proven.
This approach aligns with the repository's existing strengths: strong conventions, bilingual maintenance, plugin lifecycle complexity, and growing repository operations.

View File

@@ -0,0 +1,424 @@
# gh-aw 集成方案
> 本文档用于为 `openwebui-extensions` 仓库设计一套安全、渐进式的 GitHub Agentic Workflows (`gh-aw`) 接入方案。
---
## 1. 目标
- 在不替换现有稳定 CI 的前提下,引入具备仓库理解能力的 AI 维护层。
-`gh-aw` 用于更适合自然语言推理的任务,而不是机械脚本执行。
- 保留当前发布、部署、发布插件和统计工作流作为执行骨架。
- 为仓库维护引入可观测性、自动诊断和长期记忆能力。
---
## 2. 为什么这个仓库适合 gh-aw
本仓库已经有一套很强的确定性自动化:
- `/.github/workflows/release.yml`
- `/.github/workflows/plugin-version-check.yml`
- `/.github/workflows/deploy.yml`
- `/.github/workflows/publish_plugin.yml`
- `/.github/workflows/community-stats.yml`
这些工作流擅长精确执行,但并不擅长理解仓库规范本身。
`gh-aw` 更适合以下任务:
- 联合阅读代码、文档和 PR 描述后再做判断
- 带语义地应用仓库规范
- 生成结构化的 review 评论
- 自动分析失败的工作流运行
- 在多次运行之间保存维护经验和模式
这与当前仓库的真实需求高度匹配:
- 双语文档同步
- 插件代码、README 与 docs 一致性检查
- 跨多个文件的发布前完整性核查
- Issue 与 PR 的规模化维护
---
## 3. 非目标
第一阶段不建议让 `gh-aw`
- 替换 `release.yml`
- 替换 `publish_plugin.yml`
- 替换 MkDocs 部署
- 默认自动合并或自动推送代码
- 一开始就拥有过宽的写权限
第一阶段应把它定位为 review、诊断和 preflight 层。
---
## 4. 接入原则
### 4.1 确定性执行继续由 YAML 工作流承担
现有 YAML workflow 继续负责:
- 创建 release
- 发布插件
- 部署文档
- 提取和比较版本号
- 生成社区统计
### 4.2 Agentic workflow 只负责判断和总结
`gh-aw` workflow 优先承担:
- 基于规范的语义审查
- 发布前完整性检查
- 文档漂移巡检
- CI 失败原因分析
- Issue 分流与回复草稿生成
### 4.3 默认只读
优先使用最小权限,并通过 safe outputs 进行受控评论或低风险输出。
### 4.4 逐步扩容
一次只上线一个 agentic workflow验证质量后再扩大范围。
---
## 5. 建议的仓库结构
### 5.1 新增文件和目录
```text
.github/
├── workflows/
│ ├── release.yml
│ ├── plugin-version-check.yml
│ ├── deploy.yml
│ ├── publish_plugin.yml
│ ├── community-stats.yml
│ ├── aw-pr-maintainer-review.md
│ ├── aw-pr-maintainer-review.lock.yml
│ ├── aw-release-preflight.md
│ ├── aw-release-preflight.lock.yml
│ ├── aw-ci-audit.md
│ ├── aw-ci-audit.lock.yml
│ ├── aw-docs-drift-review.md
│ └── aw-docs-drift-review.lock.yml
├── gh-aw/
│ ├── prompts/
│ │ ├── pr-review-policy.md
│ │ ├── release-preflight-policy.md
│ │ ├── ci-audit-policy.md
│ │ └── docs-drift-policy.md
│ ├── schemas/
│ │ └── review-output-example.json
│ └── README.md
└── copilot-instructions.md
```
### 5.2 命名规范
所有 agentic workflow 源文件统一使用 `aw-` 前缀:
- `aw-pr-maintainer-review.md`
- `aw-release-preflight.md`
- `aw-ci-audit.md`
- `aw-docs-drift-review.md`
这样做的原因:
- 可以和现有手写 YAML 工作流明确区分
- 便于在仓库中快速搜索和定位
- 方便调试和发布时识别来源
### 5.3 为什么不直接替换 `.yml`
当前 `.yml` 文件承担的是生产执行逻辑。第一阶段 `gh-aw` 的角色应该是补充,而不是接管。
---
## 6. 建议优先建设的 workflow 组合
### 6.1 第一阶段PR 维护者语义审查
**文件**: `/.github/workflows/aw-pr-maintainer-review.md`
**作用**:
- 审查涉及插件、文档或开发规范的 PR
- 对缺失的仓库标准更新给出评论
- 作为 `plugin-version-check.yml` 之上的语义层
**建议检查项**:
- 插件代码修改后是否更新版本号
- 是否同时更新 `README.md``README_CN.md`
- 是否同步更新 docs 镜像页
- 是否需要更新根 README 的日期 badge
- 插件代码是否遵守 i18n 与 helper 规范
- PR 标题或正文是否符合 Conventional Commits 精神
**建议权限**:
```yaml
permissions:
contents: read
pull-requests: write
issues: write
```
**建议工具**:
- 只读型 `github:` 工具
- 只开放少量只读 `bash:` 命令
- 第一阶段不开放 `edit:`
- `agentic-workflows:` 可在后续成熟后再启用
### 6.2 第一阶段:发布前预检
**文件**: `/.github/workflows/aw-release-preflight.md`
**作用**:
- 在 release 前或手动触发时执行
-`release.yml` 打包和发布之前,先检查发布完整性
**建议检查项**:
- 代码版本号和文档版本号是否一致
- 双语 README 是否完整更新
- docs 插件镜像页是否存在并匹配当前发布目标
- release notes 来源文件是否齐全
- commit message 与 release 草案是否连贯
**输出方式**:
- 在 PR 或 issue 中写总结评论
- 可附带 checklist artifact
- 不直接执行正式发布
### 6.3 第二阶段CI 失败自动审计
**文件**: `/.github/workflows/aw-ci-audit.md`
**作用**:
- 分析 `release.yml``publish_plugin.yml``community-stats.yml` 等关键 workflow 的失败运行
- 输出根因判断和下一步修复建议
**适合 gh-aw 的原因**:
- 可以通过 `gh aw mcp-server` 使用 `logs``audit` 等能力
- 原生支持对 workflow 执行痕迹进行事后分析
### 6.4 第二阶段:文档漂移巡检
**文件**: `/.github/workflows/aw-docs-drift-review.md`
**作用**:
- 定期检查插件代码、插件目录 README、本地 docs 镜像和根索引之间是否发生漂移
**建议检查项**:
- 是否缺少 `README_CN.md`
- README 章节顺序是否偏离规范
- 插件更新后 docs 页面是否缺失
- 代码和文档中的版本号是否不一致
### 6.5 第三阶段Issue 维护助手
**候选文件**: `/.github/workflows/aw-issue-maintainer.md`
**作用**:
- 汇总长期未回复的 issue
- 生成英文或双语回复草稿
- 按插件归类重复问题
这个阶段建议在前面的 review 和 audit 流程稳定后再上线。
---
## 7. 与现有 workflow 的职责映射
| 当前 Workflow | 是否保留 | gh-aw 搭档 | 职责划分 |
|------|------|------|------|
| `/.github/workflows/release.yml` | 保留 | `aw-release-preflight.md` | `release.yml` 负责执行,`gh-aw` 负责判断是否已准备好 |
| `/.github/workflows/plugin-version-check.yml` | 保留 | `aw-pr-maintainer-review.md` | 硬性门禁 + 语义审查 |
| `/.github/workflows/deploy.yml` | 保留 | 初期不加 | 确定性构建和部署 |
| `/.github/workflows/publish_plugin.yml` | 保留 | `aw-ci-audit.md` | 确定性发布 + 失败诊断 |
| `/.github/workflows/community-stats.yml` | 保留 | `aw-ci-audit.md` | 确定性统计 + 异常诊断 |
---
## 8. 工具模型建议
### 8.1 第一阶段建议启用的内建工具
建议从窄权限工具集开始:
```yaml
tools:
github:
toolsets: [default]
bash:
- echo
- pwd
- ls
- cat
- head
- tail
- grep
- wc
- git status
- git diff
```
第一阶段不要开放完全不受限的 shell。
### 8.2 MCP 使用策略
后续可通过 `gh aw mcp-server` 引入:
- workflow `status`
- workflow `compile`
- workflow `logs`
- workflow `audit`
- `mcp-inspect`
这对 `aw-ci-audit.md` 特别有价值。
### 8.3 Safe output 策略
第一阶段仅开放低风险 safe outputs
- 给 PR 写评论
- 给 issue 写评论
- 在明确需要时创建低风险维护 issue
一开始不要让 agent 自动提交代码修改。
---
## 9. Repo Memory 策略
`gh-aw` 的 repo memory 很适合本仓库,但必须加限制。
### 9.1 第一批适合保存的内容
- 重复出现的 CI 失败模式
- 常见文档同步遗漏
- 高频 review 提醒项
- 按插件聚类的 issue 模式
### 9.2 推荐配置思路
- 只允许 `.md``.json`
- 限制 patch size
- 按主题拆成多个 memory stream
建议的逻辑布局:
```text
memory/review-notes/*.md
memory/ci-patterns/*.md
memory/issue-clusters/*.json
```
### 9.3 重要提醒
不要把 secret、token 或未公开敏感信息写入 repo memory。
---
## 10. 分阶段落地顺序
### Phase 0: 准备阶段
- 维护者本地安装 `gh-aw`
- 添加一个简短的 `/.github/gh-aw/README.md`
- 写清楚 workflow 命名规范和 review 预期
### Phase 1: 只读语义审查
- 上线 `aw-pr-maintainer-review.md`
- 上线 `aw-release-preflight.md`
- 输出先限制为总结和评论
### Phase 2: 诊断与记忆
- 上线 `aw-ci-audit.md`
- 在需要的地方启用 `agentic-workflows:`
- 为重复失败模式加入受限 `repo-memory`
### Phase 3: 维护自动化
- 增加文档漂移巡检
- 增加 issue 维护 workflow
- 只有在信号质量足够稳定后,再考虑有限度的代码修改建议
---
## 11. 维护者本地使用建议
### 11.1 安装 CLI
```bash
curl -sL https://raw.githubusercontent.com/github/gh-aw/main/install-gh-aw.sh | bash
```
### 11.2 常用命令
```bash
gh aw version
gh aw compile
gh aw status
gh aw run aw-pr-maintainer-review
gh aw logs
gh aw audit <run-id>
```
### 11.3 VS Code MCP 集成
后续可选增强项是把 `gh aw mcp-server` 加入本地 MCP 配置,这样编辑器内的 agent 会直接具备 workflow 自省能力。
---
## 12. 最小可行落地建议
建议第一步只做这两个 workflow
1. `aw-pr-maintainer-review.md`
2. `aw-release-preflight.md`
这样可以以最低风险获得最高价值的增强。
---
## 13. 成功标准
如果接入有效,应该看到这些结果:
- PR 评论更具体,更贴合仓库规范
- 发布前能更早发现文档或版本同步遗漏
- CI 失败后更快得到可执行的总结
- 维护者花在重复性规范检查上的时间下降
- 现有确定性 workflow 的核心行为保持稳定
---
## 14. 总结
`openwebui-extensions` 来说,`gh-aw` 最合适的定位是智能维护层。
- 现有 YAML workflow 继续负责执行。
- agentic workflow 负责语义审查和诊断。
- 第一阶段默认只读。
- 等输出质量稳定后再逐步放权。
这条路径和仓库现状是匹配的:规范密度高、双语维护复杂、插件生命周期长,而且已经具备成熟的 AI 工程上下文。

BIN
docs/development/image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 KiB

View File

@@ -32,6 +32,14 @@ Learn how to develop plugins and contribute to OpenWebUI Extensions.
[:octicons-arrow-right-24: Read the Plan](copilot-engineering-plan.md)
- :material-source-branch:{ .lg .middle } **gh-aw Integration Plan**
---
Adoption plan for using GitHub Agentic Workflows as a semantic review and diagnostics layer in this repository.
[:octicons-arrow-right-24: Read the Plan](gh-aw-integration-plan.md)
- :material-github:{ .lg .middle } **Contributing**
---

View File

@@ -32,6 +32,14 @@
[:octicons-arrow-right-24: 阅读文档](copilot-engineering-plan.md)
- :material-source-branch:{ .lg .middle } **gh-aw 集成方案**
---
面向本仓库的 GitHub Agentic Workflows 渐进式接入设计,重点覆盖语义审查、发布预检与 CI 诊断。
[:octicons-arrow-right-24: 阅读文档](gh-aw-integration-plan.zh.md)
- :material-github:{ .lg .middle } **贡献指南**
---

View File

@@ -1,16 +1,15 @@
# Async Context Compression Filter
**Author:** [Fu-Jie](https://github.com/Fu-Jie/openwebui-extensions) | **Version:** 1.3.0 | **Project:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **License:** MIT
**Author:** [Fu-Jie](https://github.com/Fu-Jie/openwebui-extensions) | **Version:** 1.4.0 | **Project:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **License:** MIT
This filter reduces token consumption in long conversations through intelligent summarization and message compression while keeping conversations coherent.
## What's new in 1.3.0
## What's new in 1.4.0
- **Internationalization (i18n)**: Complete localization of user-facing messages across 9 languages (English, Chinese, Japanese, Korean, French, German, Spanish, Italian).
- **Smart Status Display**: Added `token_usage_status_threshold` valve (default 80%) to intelligently control when token usage status is shown.
- **Improved Performance**: Frontend language detection and logging are optimized to be completely non-blocking, maintaining lightning-fast TTFB.
- **Copilot SDK Integration**: Automatically detects and skips compression for copilot_sdk based models to prevent conflicts.
- **Configuration**: `debug_mode` is now set to `false` by default for a quieter production experience.
- **Atomic Message Grouping**: Introduced structure-aware grouping for `assistant-tool-tool-assistant` chains to prevent "No tool call found" errors.
- **Tail Boundary Alignment**: Implemented automatic correction for truncation points to ensure they don't fall inside a tool-calling sequence.
- **Chat Session Locking**: Added a session-based lock to prevent multiple concurrent summary tasks for the same chat ID.
- **Enhanced Traceability**: Improved summary formatting to include message IDs, names, and metadata for better context tracking.
---

View File

@@ -1,18 +1,17 @@
# 异步上下文压缩过滤器
**作者:** [Fu-Jie](https://github.com/Fu-Jie/openwebui-extensions) | **版本:** 1.3.0 | **项目:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **许可证:** MIT
**作者:** [Fu-Jie](https://github.com/Fu-Jie/openwebui-extensions) | **版本:** 1.4.0 | **项目:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **许可证:** MIT
> **重要提示**:为了确保所有过滤器的可维护性和易用性,每个过滤器都应附带清晰、完整的文档,以确保其功能、配置和使用方法得到充分说明。
本过滤器通过智能摘要和消息压缩技术,在保持对话连贯性的同时,显著降低长对话的 Token 消耗。
## 1.3.0 版本更新
## 1.4.0 版本更新
- **国际化 (i18n) 支持**: 完成了所有用户可见消息的本地化,现已原生支持 9 种语言(含中、英、日、韩及欧洲主要语言)
- **智能状态显示**: 新增 `token_usage_status_threshold` 阀门(默认 80%),可以智能控制何时显示 Token 用量状态,减少不必要的打扰
- **性能大幅优化**: 对前端语言检测和日志处理流程进行了非阻塞重构完全不影响首字节响应时间TTFB保持毫秒级极速推流
- **Copilot SDK 兼容**: 自动检测并跳过基于 `copilot_sdk` 模型的上下文压缩,避免冲突
- **配置项调整**: 为了提供更安静的生产环境体验,`debug_mode` 现已默认设置为 `false`
- **原子消息组 (Atomic Grouping)**: 引入结构感知的消息分组逻辑,确保工具调用链被整体保留或移除,彻底解决 "No tool call found" 错误
- **尾部边界自动对齐**: 实现了截断点的自动修正逻辑,确保历史上下文截断不会落在工具调用序列中间
- **会话级异步锁**: 增加了基于 `chat_id` 的后台任务锁,防止同一会话并发触发多个总结任务
- **元数据溯源增强**: 优化了总结输入格式,在总结中保留了消息 ID、参与者名称及关键元数据提升上下文可追踪性
---

View File

@@ -22,7 +22,7 @@ Filters act as middleware in the message pipeline:
Reduces token consumption in long conversations through intelligent summarization while maintaining coherence.
**Version:** 1.3.0
**Version:** 1.4.0
[:octicons-arrow-right-24: Documentation](async-context-compression.md)
@@ -52,7 +52,7 @@ Filters act as middleware in the message pipeline:
Fixes common Markdown formatting issues in LLM outputs, including Mermaid syntax, code blocks, and LaTeX formulas.
**Version:** 1.2.7
**Version:** 1.2.8
[:octicons-arrow-right-24: Documentation](markdown_normalizer.md)

View File

@@ -22,7 +22,7 @@ Filter 充当消息管线中的中间件:
通过智能总结减少长对话的 token 消耗,同时保持连贯性。
**版本:** 1.3.0
**版本:** 1.4.0
[:octicons-arrow-right-24: 查看文档](async-context-compression.md)
@@ -52,7 +52,7 @@ Filter 充当消息管线中的中间件:
修复 LLM 输出中常见的 Markdown 格式问题,包括 Mermaid 语法、代码块和 LaTeX 公式。
**版本:** 1.2.7
**版本:** 1.2.8
[:octicons-arrow-right-24: 查看文档](markdown_normalizer.zh.md)

View File

@@ -1,81 +1,90 @@
# Markdown Normalizer Filter
**Author:** [Fu-Jie](https://github.com/Fu-Jie/openwebui-extensions) | **Version:** 1.2.8 | **Project:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **License:** MIT
**Author:** [Fu-Jie](https://github.com/Fu-Jie/openwebui-extensions) | **Version:** 1.2.7 | **Project:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **License:** MIT
A powerful, context-aware content normalizer filter for Open WebUI designed to fix common Markdown formatting issues in LLM outputs. It ensures that code blocks, LaTeX formulas, Mermaid diagrams, and other structural Markdown elements are rendered flawlessly, without destroying valid technical content.
A content normalizer filter for Open WebUI that fixes common Markdown formatting issues in LLM outputs. It ensures that code blocks, LaTeX formulas, Mermaid diagrams, and other Markdown elements are rendered correctly.
> 🏆 **Featured by OpenWebUI Official** — This plugin was recommended in the official OpenWebUI Community Newsletter: [January 28, 2026](https://openwebui.com/blog/newsletter-january-28-2026)
> 🏆 **Featured by OpenWebUI Official** — Recommended in the official OpenWebUI Community Newsletter: [January 28, 2026](https://openwebui.com/blog/newsletter-january-28-2026)
[English](https://github.com/Fu-Jie/openwebui-extensions/blob/main/plugins/filters/markdown_normalizer/README.md) | [简体中文](https://github.com/Fu-Jie/openwebui-extensions/blob/main/plugins/filters/markdown_normalizer/README_CN.md)
## 🔥 What's New in v1.2.7
---
* **LaTeX Formula Protection**: Enhanced escape character cleaning to protect LaTeX commands like `\times`, `\nu`, and `\theta` from being corrupted.
* **Expanded i18n Support**: Now supports 12 languages with automatic detection and fallback.
* **Valves Optimization**: Optimized configuration descriptions to be English-only for better consistency.
* **Bug Fixes**:
* Resolved [Issue #49](https://github.com/Fu-Jie/openwebui-extensions/issues/49): Fixed a bug where consecutive bold parts on the same line caused spaces between them to be removed.
* Fixed a `NameError` in the plugin code that caused test collection failures.
## 🔥 What's New in v1.2.8
* **Safe-by-Default Strategy**: The `enable_escape_fix` feature is now **disabled by default**. This prevents unwanted modifications to valid technical text like Windows file paths (`C:\new\test`) or complex LaTeX formulas.
* **LaTeX Parsing Fix**: Improved the logic for identifying display math (`$$ ... $$`). Fixed a bug where LaTeX commands starting with `\n` (like `\nabla`) were incorrectly treated as newlines.
* **Reliability Enhancement**: Complete error fallback mechanism. Guarantees 0% data loss during processing.
* **Inline Code Protection**: Upgraded escaping logic to protect inline code blocks (`` `...` ``).
* **Code Block Escaping Control**: The `enable_escape_fix_in_code_blocks` Valve now correctly targets broken newlines inside code blocks (perfect for fixing flat SQL queries) when enabled.
* **Privacy Optimization**: `show_debug_log` now defaults to `False` to prevent console noise.
---
## 🚀 Why do you need this plugin? (What does it do?)
Language Models (LLMs) often generate malformed Markdown due to tokenization artifacts, aggressive escaping, or hallucinated formatting. If you've ever seen:
- A `mermaid` diagram fail to render because of missing quotes around labels.
- A SQL block stuck on a single line because `\n` was output literally instead of a real newline.
- A `<details>` block break the entire chat rendering because of missing newlines.
- A LaTeX formula fail because the LLM used `\[` instead of `$$`.
**This plugin automatically intercepts the LLM's raw output, analyzes its structure, and surgically repairs these formatting errors in real-time before they reach your browser.**
## ✨ Comprehensive Feature List
### 1. Advanced Structural Protections (Context-Aware)
Before making any changes, the plugin builds a semantic map of the text to protect your technical content:
- **Code Block Protection**: Skips formatting inside ` ``` ` code blocks by default to protect code logic.
- **Inline Code Protection**: Recognizes `` `code` `` snippets and protects regular expressions and file paths (e.g., `C:\Windows`) from being incorrectly unescaped.
- **LaTeX Protection**: Identifies inline (`$`) and block (`$$`) formulas to prevent modifying critical math commands like `\times`, `\theta`, or `\nu`.
### 2. Auto-Healing Transformations
- **Details Tag Normalization**: `<details>` blocks (often used for Chain of Thought) require strict spacing to render correctly. The plugin automatically injects blank lines after `</details>` and self-closing `<details />` tags.
- **Mermaid Syntax Fixer**: One of the most common LLM errors is omitting quotes in Mermaid diagrams (e.g., `A --> B(Some text)`). This plugin parses the Mermaid syntax and auto-quotes labels and citations to guarantee the graph renders.
- **Emphasis Spacing Fix**: Fixes formatting-breaking extra spaces inside bold/italic markers (e.g., `** text **` becomes `**text**`) while cleverly ignoring math expressions like `2 * 3 * 4`.
- **Intelligent Escape Character Cleanup**: Removes excessive literal `\n` and `\t` generated by some models and converts them to actual structural newlines (only in safe text areas).
- **LaTeX Standardization**: Automatically upgrades old-school LaTeX delimiters (`\[...\]` and `\(...\)`) to modern Markdown standards (`$$...$$` and `$ ... $`).
- **Thought Tag Unification**: Standardizes various model thought outputs (`<think>`, `<thinking>`) into a unified `<thought>` tag.
- **Broken Code Block Repair**: Fixes indentation issues, repairs mangled language prefixes (e.g., ` ```python`), and automatically closes unclosed code blocks if a generation was cut off.
- **List & Table Formatting**: Injects missing newlines to repair broken numbered lists and adds missing closing pipes (`|`) to tables.
- **XML Artifact Cleanup**: Silently removes leftover `<antArtifact>` or `<antThinking>` tags often leaked by Claude models.
### 3. Reliability & Safety
- **100% Rollback Guarantee**: If any normalization logic fails or crashes, the plugin catches the error and silently returns the exact original text, ensuring your chat never breaks.
## 🌐 Multilingual Support
Supports automatic interface and status switching for the following languages:
The plugin UI and status notifications automatically switch based on your language:
`English`, `简体中文`, `繁體中文 (香港)`, `繁體中文 (台灣)`, `한국어`, `日本語`, `Français`, `Deutsch`, `Español`, `Italiano`, `Tiếng Việt`, `Bahasa Indonesia`.
## ✨ Core Features
* **Details Tag Normalization**: Ensures proper spacing for `<details>` tags (used for thought chains). Adds a blank line after `</details>` and ensures a newline after self-closing `<details />` tags to prevent rendering issues.
* **Emphasis Spacing Fix**: Fixes extra spaces inside emphasis markers (e.g., `** text **` -> `**text**`) which can cause rendering failures. Includes safeguards to protect math expressions (e.g., `2 * 3 * 4`) and list variables.
* **Mermaid Syntax Fix**: Automatically fixes common Mermaid syntax errors, such as unquoted node labels (including multi-line labels and citations) and unclosed subgraphs. **New in v1.1.2**: Comprehensive protection for edge labels (text on connecting lines) across all link types (solid, dotted, thick).
* **Frontend Console Debugging**: Supports printing structured debug logs directly to the browser console (F12) for easier troubleshooting.
* **Code Block Formatting**: Fixes broken code block prefixes, suffixes, and indentation.
* **LaTeX Normalization**: Standardizes LaTeX formula delimiters (`\[` -> `$$`, `\(` -> `$`).
* **Thought Tag Normalization**: Unifies thought tags (`<think>`, `<thinking>` -> `<thought>`).
* **Escape Character Fix**: Cleans up excessive escape characters (`\\n`, `\\t`).
* **List Formatting**: Ensures proper newlines in list items.
* **Heading Fix**: Adds missing spaces in headings (`#Heading` -> `# Heading`).
* **Table Fix**: Adds missing closing pipes in tables.
* **XML Cleanup**: Removes leftover XML artifacts.
## How to Use 🛠️
1. Install the plugin in Open WebUI.
2. Enable the filter globally or for specific models.
3. Configure the enabled fixes in the **Valves** settings.
4. (Optional) **Show Debug Log** is enabled by default in Valves. This prints structured logs to the browser console (F12).
> [!WARNING]
> As this is an initial version, some "negative fixes" might occur (e.g., breaking valid Markdown). If you encounter issues, please check the console logs, copy the "Original" vs "Normalized" content, and submit an issue.
2. Enable the filter globally or assign it to specific models (highly recommended for models with poor formatting).
3. Tune the specific fixes you want via the **Valves** settings.
## Configuration (Valves) ⚙️
| Parameter | Default | Description |
| :--- | :--- | :--- |
| `priority` | `50` | Filter priority. Higher runs later (recommended after other filters). |
| `enable_escape_fix` | `True` | Fix excessive escape characters (`\n`, `\t`, etc.). |
| `enable_escape_fix_in_code_blocks` | `False` | Apply escape fix inside code blocks (may affect valid code). |
| `enable_thought_tag_fix` | `True` | Normalize thought tags (`</thought>`). |
| `enable_details_tag_fix` | `True` | Normalize `<details>` tags and add safe spacing. |
| `enable_code_block_fix` | `True` | Fix code block formatting (indentation/newlines). |
| `enable_latex_fix` | `True` | Normalize LaTeX delimiters (`\[` -> `$$`, `\(` -> `$`). |
| `priority` | `50` | Filter priority. Higher runs later (recommended to run this after all other content filters). |
| `enable_escape_fix` | `False` | Convert excessive literal escape characters (`\n`, `\t`) to real spacing. (Default: False for safety). |
| `enable_escape_fix_in_code_blocks` | `False` | **Pro-tip**: Turn this ON if your SQL/HTML code blocks are constantly printing on a single line. Turn OFF for Python/C++. |
| `enable_thought_tag_fix` | `True` | Normalize `<think>` tags. |
| `enable_details_tag_fix` | `True` | Normalize `<details>` spacing. |
| `enable_code_block_fix` | `True` | Fix code block indentation and newlines. |
| `enable_latex_fix` | `True` | Standardize LaTeX delimiters (`\[` -> `$$`). |
| `enable_list_fix` | `False` | Fix list item newlines (experimental). |
| `enable_unclosed_block_fix` | `True` | Auto-close unclosed code blocks. |
| `enable_fullwidth_symbol_fix` | `False` | Fix full-width symbols in code blocks. |
| `enable_mermaid_fix` | `True` | Fix common Mermaid syntax errors. |
| `enable_heading_fix` | `True` | Fix missing space in headings. |
| `enable_table_fix` | `True` | Fix missing closing pipe in tables. |
| `enable_xml_tag_cleanup` | `True` | Cleanup leftover XML tags. |
| `enable_emphasis_spacing_fix` | `False` | Fix extra spaces in emphasis. |
| `show_status` | `True` | Show status notification when fixes are applied. |
| `show_debug_log` | `True` | Print debug logs to browser console (F12). |
| `enable_mermaid_fix` | `True` | Fix common Mermaid syntax errors (auto-quoting). |
| `enable_heading_fix` | `True` | Add missing space after heading hashes (`#Title` -> `# Title`). |
| `enable_table_fix` | `True` | Add missing closing pipe in tables. |
| `enable_xml_tag_cleanup` | `True` | Remove leftover XML artifacts. |
| `enable_emphasis_spacing_fix` | `False` | Fix extra spaces in emphasis formatting. |
| `show_status` | `True` | Show UI status notification when a fix is actively applied. |
| `show_debug_log` | `False` | Print detailed before/after diffs to browser console (F12). |
## ⭐ Support
If this plugin has been useful, a star on [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) is a big motivation for me. Thank you for the support.
If this plugin saves your day, a star on [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) is a big motivation for me. Thank you!
## 🧩 Others
### Troubleshooting ❓
* **Submit an Issue**: If you encounter any problems, please submit an issue on GitHub: [OpenWebUI Extensions Issues](https://github.com/Fu-Jie/openwebui-extensions/issues)
### Changelog
See the full history on GitHub: [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions)
* **Troubleshooting**: Encountering "negative fixes"? Enable `show_debug_log`, check your console, and submit an issue on GitHub: [OpenWebUI Extensions Issues](https://github.com/Fu-Jie/openwebui-extensions/issues)

View File

@@ -1,81 +1,89 @@
# Markdown 格式化过滤器 (Markdown Normalizer)
**作者:** [Fu-Jie](https://github.com/Fu-Jie/openwebui-extensions) | **Version:** 1.2.7 | **项目:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **许可证:** MIT
**作者:** [Fu-Jie](https://github.com/Fu-Jie/openwebui-extensions) | **版本:** 1.2.8 | **项目:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **许可证:** MIT
这是一个用于 Open WebUI 的内容格式化过滤器,旨在修复 LLM 输出中常见的 Markdown 格式问题。它能确保代码块、LaTeX 公式、Mermaid 图表和其他 Markdown 元素被正确渲染
这是一个强大的、具备上下文感知的 Markdown 内容规范化过滤器,专为 Open WebUI 设计,旨在实时修复大语言模型 (LLM) 输出中常见的格式错乱问题。它能确保代码块、LaTeX 公式、Mermaid 图表以及其他结构化元素被完美渲染,同时**绝不破坏**你原有的有效技术内容(如代码、正则、路径)
> 🏆 **OpenWebUI 官方推荐** — 获得 OpenWebUI 社区 Newsletter 官方推荐:[2026 年 1 月 28 日](https://openwebui.com/blog/newsletter-january-28-2026)
> 🏆 **OpenWebUI 官方推荐** — 本插件获得 OpenWebUI 社区 Newsletter 官方推荐:[2026 年 1 月 28 日](https://openwebui.com/blog/newsletter-january-28-2026)
## 🔥 最新更新 v1.2.7
[English](https://github.com/Fu-Jie/openwebui-extensions/blob/main/plugins/filters/markdown_normalizer/README.md) | [简体中文](https://github.com/Fu-Jie/openwebui-extensions/blob/main/plugins/filters/markdown_normalizer/README_CN.md)
* **LaTeX 公式保护**: 增强了转义字符清理逻辑,自动保护 `$ $``$$ $$` 内的 LaTeX 命令(如 `\times``\nu``\theta`),防止渲染失效。
* **扩展国际化 (i18n) 支持**: 现已支持 12 种语言,具备自动探测与回退机制。
* **配置项优化**: 将 Valves 配置项的描述统一为英文,保持界面一致性。
* **修复 Bug**:
* 修复了 [Issue #49](https://github.com/Fu-Jie/openwebui-extensions/issues/49):解决了当同一行存在多个加粗部分时,由于正则匹配过于贪婪导致中间内容丢失空格的问题
* 修复了插件代码中的 `NameError` 错误,确保测试脚本能正常运行
---
## 🔥 最新更新 v1.2.8
* **“默认安全”策略 (Safe-by-Default)**`enable_escape_fix` 功能现在**默认禁用**。这能有效防止插件在未经授权的情况下误改 Windows 路径 (`C:\new\test`) 或复杂的 LaTeX 公式。
* **LaTeX 解析优化**:重构了显示数学公式 (`$$ ... $$`) 的识别逻辑。修复了 LaTeX 命令如果以 `\n` 开头(如 `\nabla`)会被错误识别为换行符的 Bug
* **可靠性增强**:实现了完整的错误回滚机制。当修复过程发生意外错误时,保证 100% 返回原始文本,不丢失任何数据
* **配置项修复**`enable_escape_fix_in_code_blocks` 配置项现在能正确作用于代码块了。**如果您遇到 SQL 挤在一行的问题,只需在设置中手动开启此项即可。**
---
## 🚀 为什么你需要这个插件?(它能解决什么问题?)
由于分词 (Tokenization) 伪影、过度转义或格式幻觉LLM 经常会生成破损的 Markdown。如果你遇到过以下情况
- `mermaid` 图表因为节点标签缺少双引号而渲染失败、白屏。
- LLM 输出的 SQL 语句挤在一行,因为本该换行的地方输出了字面量 `\n`
- 复杂的 `<details>` (思维链展开块) 因为缺少换行符导致整个聊天界面排版崩塌。
- LaTeX 数学公式无法显示,因为模型使用了旧版的 `\[` 而不是 Markdown 支持的 `$$`
**本插件会自动拦截 LLM 返回的原始数据,实时分析其文本结构,并像外科手术一样精准修复这些排版错误,然后再将其展示在你的浏览器中。**
## ✨ 核心功能与修复能力全景
### 1. 高级结构保护 (上下文感知)
在执行任何修改前,插件会为整个文本建立语义地图,确保技术性内容不被误伤:
- **代码块保护**:默认跳过 ` ``` ` 内部的内容,保护所有编程逻辑。
- **行内代码保护**:识别 `` `代码` `` 片段,防止正则表达式(如 `[\n\r]`)或文件路径(如 `C:\Windows`)被错误地去转义。
- **LaTeX 公式保护**:识别行内 (`$`) 和块级 (`$$`) 公式,防止诸如 `\times`, `\theta` 等核心数学命令被意外破坏。
### 2. 自动治愈转换 (Auto-Healing)
- **Details 标签排版修复**`<details>` 块要求极为严格的空行才能正确渲染内部内容。插件会自动在 `</details>` 以及自闭合 `<details />` 标签后注入安全的换行符。
- **Mermaid 语法急救**:自动修复最常见的 Mermaid 错误——为未加引号的节点标签(如 `A --> B(Some text)`)自动补充双引号,甚至支持多行标签和引用,确保拓扑图 100% 渲染。
- **强调语法间距修复**:修复加粗/斜体语法内部多余的空格(如 `** 文本 **` 变为 `**文本**`,否则 OpenWebUI 无法加粗),同时智能忽略数学算式(如 `2 * 3 * 4`)。
- **智能转义字符清理**:将模型过度转义生成的字面量 `\n``\t` 转化为真正的换行和缩进(仅在安全的纯文本区域执行)。
- **LaTeX 现代化转换**:自动将旧式的 LaTeX 定界符(`\[...\]``\(...\)`)升级为现代 Markdown 标准(`$$...$$``$ ... $`)。
- **思维标签大一统**:无论模型输出的是 `<think>` 还是 `<thinking>`,统一标准化为 `<thought>` 标签。
- **残缺代码块修复**:修复乱码的语言前缀(例如 ` ```python`),调整缩进,并在模型回答被截断时,自动补充闭合的 ` ``` `
- **列表与表格急救**:为粘连的编号列表注入换行,为残缺的 Markdown 表格补充末尾的闭合管道符(`|`)。
- **XML 伪影消除**:静默移除 Claude 模型经常泄露的 `<antArtifact>``<antThinking>` 残留标签。
### 3. 绝对的可靠性与安全 (100% Rollback)
- **无损回滚机制**:如果在修复过程中发生任何意外错误或崩溃,插件会立即捕获异常,并静默返回**绝对原始**的文本,确保你的对话永远不会因插件报错而丢失。
## 🌐 多语言支持 (i18n)
支持以下语言的界面状态自动切换:
界面状态提示气泡会根据你的浏览器语言自动切换:
`English`, `简体中文`, `繁體中文 (香港)`, `繁體中文 (台灣)`, `한국어`, `日本語`, `Français`, `Deutsch`, `Español`, `Italiano`, `Tiếng Việt`, `Bahasa Indonesia`
## ✨ 核心特性
* **Details 标签规范化**: 确保 `<details>` 标签(常用于思维链)有正确的间距。在 `</details>` 后添加空行,并在自闭合 `<details />` 标签后添加换行,防止渲染问题。
* **强调空格修复**: 修复强调标记内部的多余空格(例如 `** 文本 **` -> `**文本**`),这会导致 Markdown 渲染失败。包含保护机制,防止误修改数学表达式(如 `2 * 3 * 4`)或列表变量。
* **Mermaid 语法修复**: 自动修复常见的 Mermaid 语法错误,如未加引号的节点标签(支持多行标签和引用标记)和未闭合的子图 (Subgraph)。**v1.1.2 新增**: 全面保护各种类型的连线标签(实线、虚线、粗线),防止被误修改。
* **前端控制台调试**: 支持将结构化的调试日志直接打印到浏览器控制台 (F12),方便排查问题。
* **代码块格式化**: 修复破损的代码块前缀、后缀和缩进问题。
* **LaTeX 规范化**: 标准化 LaTeX 公式定界符 (`\[` -> `$$`, `\(` -> `$`)。
* **思维标签规范化**: 统一思维链标签 (`<think>`, `<thinking>` -> `<thought>`)。
* **转义字符修复**: 清理过度的转义字符 (`\\n`, `\\t`)。
* **列表格式化**: 确保列表项有正确的换行。
* **标题修复**: 修复标题中缺失的空格 (`#标题` -> `# 标题`)。
* **表格修复**: 修复表格中缺失的闭合管道符。
* **XML 清理**: 移除残留的 XML 标签。
## 使用方法
## 使用方法 🛠️
1. 在 Open WebUI 中安装此插件。
2. 全局启用或为特定模型启用此过滤器。
3.**Valves** 设置中配置需要启用的修复项。
4. (可选) **显示调试日志 (Show Debug Log)** 在 Valves 中默认开启。这会将结构化的日志打印到浏览器控制台 (F12)。
> [!WARNING]
> 由于这是初版,可能会出现“负向修复”的情况(例如破坏了原本正确的格式)。如果您遇到问题,请务目查看控制台日志,复制“原始 (Original)”与“规范化 (Normalized)”的内容对比,并提交 Issue 反馈。
2. 全局启用或为特定模型启用此过滤器(强烈建议为格式输出不稳定的模型启用)
3.**Valves (配置参数)** 设置中微调你需要的修复项。
## 配置参数 (Valves) ⚙️
| 参数 | 默认值 | 描述 |
| :--- | :--- | :--- |
| `priority` | `50` | 过滤器优先级。数值越大越靠后(建议在其他过滤器之后运行)。 |
| `enable_escape_fix` | `True` | 修复过度的转义字符(`\n`, `\t` 等)。 |
| `enable_escape_fix_in_code_blocks` | `False` | 在代码块内应用转义修复(可能影响有效代码)。 |
| `enable_thought_tag_fix` | `True` | 规范化思维标签`</thought>`。 |
| `enable_details_tag_fix` | `True` | 规范化 `<details>` 标签并添加安全间距。 |
| `enable_code_block_fix` | `True` | 修复代码块格式(缩进/换行。 |
| `enable_latex_fix` | `True` | 规范化 LaTeX 定界符(`\[` -> `$$`, `\(` -> `$`)。 |
| `priority` | `50` | 过滤器优先级。数值越大越靠后(建议在其他内容过滤器之后运行)。 |
| `enable_escape_fix` | `False` | 修复过度的转义字符(将字面量 `\n` 转换为实际换行)。**默认禁用以保证安全。** |
| `enable_escape_fix_in_code_blocks` | `False` | **高阶技巧**:如果你的 SQL 或 HTML 代码块总是挤在一行,**请开启此项**。如果你经常写 Python/C++,建议保持关闭。 |
| `enable_thought_tag_fix` | `True` | 规范化思维标签`<thought>`。 |
| `enable_details_tag_fix` | `True` | 修复 `<details>` 标签的排版间距。 |
| `enable_code_block_fix` | `True` | 修复代码块前缀、缩进换行。 |
| `enable_latex_fix` | `True` | 规范化 LaTeX 定界符(`\[` -> `$$`)。 |
| `enable_list_fix` | `False` | 修复列表项换行(实验性)。 |
| `enable_unclosed_block_fix` | `True` | 自动闭合未闭合的代码块。 |
| `enable_fullwidth_symbol_fix` | `False` | 修复代码块中的全角符号。 |
| `enable_mermaid_fix` | `True` | 修复常见 Mermaid 语法错误。 |
| `enable_heading_fix` | `True` | 修复标题中缺失的空格。 |
| `enable_unclosed_block_fix` | `True` | 自动闭合被截断的代码块。 |
| `enable_mermaid_fix` | `True` | 修复常见 Mermaid 语法错误(如自动加引号)。 |
| `enable_heading_fix` | `True` | 修复标题中缺失的空格 (`#Title` -> `# Title`)。 |
| `enable_table_fix` | `True` | 修复表格中缺失的闭合管道符。 |
| `enable_xml_tag_cleanup` | `True` | 清理残留的 XML 标签。 |
| `enable_emphasis_spacing_fix` | `False` | 修复强调语法的多余空格。 |
| `show_status` | `True` | 应用修复时显示状态通知。 |
| `show_debug_log` | `True` | 在浏览器控制台打印调试日志。 |
| `enable_xml_tag_cleanup` | `True` | 清理残留的 XML 分析标签。 |
| `enable_emphasis_spacing_fix` | `False` | 修复强调语法(加粗/斜体)内部的多余空格。 |
| `show_status` | `True` | 当触发任何修复规则时,在页面底部显示提示气泡。 |
| `show_debug_log` | `False` | 在浏览器控制台 (F12) 打印修改前后的详细对比日志。 |
## ⭐ 支持
如果这个插件拯救了你的排版,欢迎到 [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) 点个 Star这是我持续改进的最大动力。感谢支持
如果这个插件对你有帮助,欢迎到 [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) 点个 Star这将是我持续改进的动力感谢支持。
## 其他
### 故障排除 (Troubleshooting) ❓
* **提交 Issue**: 如果遇到任何问题,请在 GitHub 上提交 Issue[OpenWebUI Extensions Issues](https://github.com/Fu-Jie/openwebui-extensions/issues)
### 更新日志
完整历史请查看 GitHub 项目: [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions)
## 🧩 其他
* **故障排除**:遇到“负向修复”(即原本正常的排版被修坏了)?请开启 `show_debug_log`,在 F12 控制台复制出原始文本,并在 GitHub 提交 Issue[提交 Issue](https://github.com/Fu-Jie/openwebui-extensions/issues)

View File

@@ -1,6 +1,6 @@
# GitHub Copilot SDK Pipe for OpenWebUI
**Author:** [Fu-Jie](https://github.com/Fu-Jie) | **Version:** 0.9.1 | **Project:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **License:** MIT
**Author:** [Fu-Jie](https://github.com/Fu-Jie) | **Version:** 0.10.0 | **Project:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **License:** MIT
This is a powerful **GitHub Copilot SDK** Pipe for **OpenWebUI** that provides a unified **Agentic experience**. It goes beyond simple model access by enabling autonomous **Intent Recognition**, **Web Search**, and **Context Compaction**. It seamlessly reuses your existing **Tools, MCP servers, OpenAPI servers, and Skills** from OpenWebUI to create a truly integrated ecosystem.
@@ -20,13 +20,14 @@ This is a powerful **GitHub Copilot SDK** Pipe for **OpenWebUI** that provides a
---
## ✨ v0.9.1: Autonomous Web Search & Reliability Fix
## ✨ v0.10.0: Native Prompt Restoration, Live TODO Widget & SDK v0.1.30
- **🌐 Autonomous Web Search**: `web_search` is now always enabled for the Agent (bypassing the UI toggle), leveraging the Copilot SDK's native ability to decide when to search.
- **🛠️ Terminology Alignment**: Standardized all references to **"Agent"** and **"Context Compaction"** (for Infinite Session) across all languages to better reflect the technical capabilities.
- **🌐 Language Consistency**: System prompts mandate that Agent output language remains strictly consistent with user input.
- **🐛 Fixed MCP Tool Filtering**: Resolved a critical issue where configuring `function_name_filter_list` (or selecting specific tools in UI) would cause all tools from that MCP server to be incorrectly hidden due to ID prefix mismatches (`server:mcp:`).
- **🔍 Improved Filter Stability**: Ensured tool-level whitelists apply reliably without breaking the entire server connection.
- **⌨️ Authentic Prompt Restoration**: Most native Copilot CLI prompts have been restored to ensure authentic behavior and enhanced capabilities across the Agentic workflow.
- **📋 Live TODO Widget**: Added a compact real-time task tracking widget synchronized with `session.db`, keeping in-progress work visible without cluttering the chat history.
- **🧩 OpenWebUI Tool Call Fixes**: Fixed custom tool invocation by syncing injected context with OpenWebUI 0.8.x expectations, including `__request__`, `request`, `body`, `__messages__`, `__metadata__`, `__files__`, `__task__`, and session/chat/message IDs.
- **🔒 SDK v0.1.30 + Adaptive Workstyle**: Upgraded the pipe to `github-copilot-sdk==0.1.30`, moving workflow logic into the system prompt for autonomous "Plan-vs-Execute" decisions.
- **🐛 Intent + Widget UX Fixes**: Fixed `report_intent` localization and cleaned up TODO widget layout for a more professional look.
- **🧾 Better Embedded Tool Results**: Improved HTML/embedded tool outcomes and synchronized documentation surface.
---
@@ -39,6 +40,7 @@ This is a powerful **GitHub Copilot SDK** Pipe for **OpenWebUI** that provides a
- **OpenAPI Bridge**: Connect to any external REST API as an Agent tool.
- **OpenWebUI Native**: Zero-config bridge to your existing OpenWebUI tools and built-ins (Web Search, Memory, etc.).
- **🧩 OpenWebUI Skills Bridge**: Transforms simple OpenWebUI Markdown instructions into powerful SDK skill folders complete with supporting scripts, templates, and data.
- **🧭 Adaptive Planning and Execution**: The Agent decides whether to respond with a planning-first analysis or direct implementation flow based on task complexity, ambiguity, and user intent.
- **♾️ Infinite Session Management**: Advanced context window management with automatic "Compaction" (summarization + list persistence). Carry out weeks-long projects without losing the core thread.
- **📊 Interactive Artifacts & Publishing**:
- **Live HTML/JS**: Instantly render and interact with apps, dashboards, or reports generated by the Agent.
@@ -49,7 +51,7 @@ This is a powerful **GitHub Copilot SDK** Pipe for **OpenWebUI** that provides a
> [!TIP]
> **💡 Visualization Pro-Tip**
> To get the most out of **HTML Artifacts** and **RichUI**, we highly recommend asking the Agent to install the skill via its GitHub URL:
> "Install this skill: https://github.com/nicobailon/visual-explainer".
> "Install this skill: <https://github.com/nicobailon/visual-explainer>".
> This skill is specifically optimized for generating high-quality visual components and integrates perfectly with this Pipe.
---
@@ -81,7 +83,6 @@ Administrators define the default behavior for all users in the function setting
| `ENABLE_MCP_SERVER` | `True` | Enable Direct MCP Client connection (Recommended). |
| `ENABLE_OPENWEBUI_SKILLS` | `True` | Enable bidirectional sync with OpenWebUI Workspace > Skills. |
| `OPENWEBUI_SKILLS_SHARED_DIR` | `/app/backend/data/cache/copilot-openwebui-skills` | Shared cache directory for skills. |
| `GITHUB_SKILLS_SOURCE_URL` | `""` | Optional GitHub tree URL for batch skill import (e.g., anthropic/skills). |
| `DISABLED_SKILLS` | `""` | Comma-separated skill names to disable in SDK session. |
| `REASONING_EFFORT` | `medium` | Reasoning effort level: low, medium, high. |
| `SHOW_THINKING` | `True` | Show model reasoning/thinking process. |
@@ -107,7 +108,6 @@ Standard users can override these settings in their individual Profile/Function
| `MAX_MULTIPLIER` | Maximum allowed billing multiplier override. |
| `EXCLUDE_KEYWORDS` | Exclude models containing these keywords. |
| `ENABLE_OPENWEBUI_SKILLS` | Enable loading all active OpenWebUI skills readable by you into SDK `SKILL.md` directories. |
| `GITHUB_SKILLS_SOURCE_URL` | Optional GitHub tree URL for batch skill import in your own session. |
| `DISABLED_SKILLS` | Comma-separated skill names to disable for your own session. |
| `BYOK_API_KEY` | Use your personal OpenAI/Anthropic API Key. |

View File

@@ -1,6 +1,6 @@
# GitHub Copilot Official SDK Pipe
**作者:** [Fu-Jie](https://github.com/Fu-Jie/openwebui-extensions) | **版本:** 0.9.1 | **项目:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **许可证:** MIT
**作者:** [Fu-Jie](https://github.com/Fu-Jie/openwebui-extensions) | **版本:** 0.10.0 | **项目:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **许可证:** MIT
这是一个将 **GitHub Copilot SDK** 深度集成到 **OpenWebUI** 中的强大 Agent SDK 管道。它不仅实现了 SDK 的核心功能,还支持 **智能意图识别**、**自主网页搜索** 与 **自动上下文压缩**,并能够无缝读取 OpenWebUI 已有的配置进行智能注入,让 Agent 能够具备以下能力:
@@ -21,13 +21,14 @@
---
## ✨ 0.9.1 最新更新:自主网页搜索与可靠性修复
## ✨ v0.10.0 最新更新:原生提示词恢复、Live TODO 小组件与 SDK v0.1.30 完善
- **🌐 强化自主网页搜索**`web_search` 工具现已强制对 Agent 开启(绕过 UI 网页搜索开关),充分利用 Copilot 自身具备的搜索判断能力。
- **🛠️ 术语一致性优化**:全语种同步将“助手”更改为 **"Agent"**,并将“优化会话”统一为 **"压缩上下文"**,更准确地描述 Infinite Session 的技术本质
- **🌐 语言一致性**:内置指令确保 Agent 输出语言与用户输入严格对齐,提供无缝的国际化交互体验
- **🐛 修复 MCP 工具过滤逻辑**:解决了在管理员后端配置 `function_name_filter_list`(或在聊天界面勾选特定工具)时,因 ID 前缀(`server:mcp:`)识别逻辑错误导致工具意外失效的问题
- **🔍 提升过滤稳定性**:修复了工具 ID 归一化逻辑,确保点选的工具白名单在 SDK 会话中精确生效
- **⌨️ 原生提示词恢复**:恢复了大部分 Copilot CLI 原生提示词,确保 Agent 在处理复杂任务时具备最正宗的行为逻辑与增强能力。
- **📋 Live TODO 小组件**:新增基于 `session.db` 实时任务状态的紧凑型嵌入式 TODO 小组件,任务进度常驻可见,无需在正文中重复显示全部待办列表
- **🧩 OpenWebUI 工具调用修复**:修复自定义工具调用时上下文注入不完整的问题,完全对齐 OpenWebUI 0.8.x 所需的系统级上下文(`__request__``body``__metadata__` 等)
- **🔒 SDK v0.1.30 与自适应工作流**:升级到 `github-copilot-sdk==0.1.30`,将规划与执行逻辑移至系统提示词,让 Agent 根据任务复杂度自主决策工作流
- **🐛 意图与体验优化**:修复 `report_intent` 国际化问题,优化 TODO 小组件的视觉布局,减少冗余空白
- **🧾 嵌入结果与文档更新**:改进 HTML/嵌入式工具结果处理,同步中英 README 与 docs 镜像页,确保发布状态一致。
---
@@ -40,6 +41,7 @@
- **OpenAPI 桥接**: 将任何外部 REST API 一键转换为 Agent 可调用的工具。
- **OpenWebUI 原生桥接**: 零配置接入现有的 OpenWebUI 工具及内置功能(网页搜索、记忆等)。
- **🧩 OpenWebUI Skills 桥接**: 将简单的 OpenWebUI Markdown 指令转化为包含脚本、模板 and 数据的强大 SDK 技能文件夹。
- **🧭 自适应规划与执行**: Agent 会根据任务复杂度、歧义程度和用户意图,自主决定先输出结构化方案,还是直接分析、实现并验证。
- **♾️ 无限会话管理**: 先进的上下文窗口管理,支持自动“压缩”(摘要提取 + TODO 列表持久化)。支持长达数周的项目跟踪而不会丢失核心上下文。
- **📊 交互式产物与发布**:
- **实时 HTML/JS**: 瞬间渲染并交互 Agent 生成的应用程序、可视化看板或报告。
@@ -50,7 +52,7 @@
> [!TIP]
> **💡 增强渲染建议**
> 为了获得最精美的 **HTML Artifacts** 与 **RichUI** 效果,建议在对话中通过提供的 GitHub 链接直接命令 Agent 安装:
> “请安装此技能https://github.com/nicobailon/visual-explainer”。
> “请安装此技能:<https://github.com/nicobailon/visual-explainer”。>
> 该技能专为生成高质量可视化组件而设计,能够与本 Pipe 完美协作。
---
@@ -67,32 +69,81 @@
---
## 🚀 快速开始 (Quick Start)
## ⚙️ 核心配置 (Valves)
1. **安装本插件**: 在 OpenWebUI 管道管理界面添加并启用。
2. **安装 [Files Filter](https://openwebui.com/posts/403a62ee-a596-45e7-be65-fab9cc249dd6)** (必须): 以获得文件处理能力。
3. **配置凭据**:
- **官方模式**: 默认即可。确保环境中安装了 `github-copilot-sdk`
- **BYOK 模式**: 填入 OpenAI/Anthropic/DeepSeek 的 Base URL 与 Key。
4. **选择模型**: 在聊天界面选择 `GitHub Copilot Official SDK Pipe` 系列模型。
5. **开始对话**: 直接上传文件或发送复杂指令。
### 1. 管理员设置(全局默认)
管理员可在函数设置中为所有用户定义默认行为。
| Valve | 默认值 | 描述 |
| :--- | :--- | :--- |
| `GH_TOKEN` | `""` | 全局 GitHub Fine-grained Token需要 `Copilot Requests` 权限。 |
| `COPILOTSDK_CONFIG_DIR` | `/app/backend/data/.copilot` | SDK 配置与会话状态的持久化目录。 |
| `ENABLE_OPENWEBUI_TOOLS` | `True` | 启用 OpenWebUI Tools 与 Built-in Tools。 |
| `ENABLE_OPENAPI_SERVER` | `True` | 启用 OpenAPI Tool Server 连接。 |
| `ENABLE_MCP_SERVER` | `True` | 启用 MCP Server 连接。 |
| `ENABLE_OPENWEBUI_SKILLS` | `True` | 启用 OpenWebUI Skills 到 SDK 技能目录的同步。 |
| `OPENWEBUI_SKILLS_SHARED_DIR` | `/app/backend/data/cache/copilot-openwebui-skills` | Skills 共享缓存目录。 |
| `DISABLED_SKILLS` | `""` | 逗号分隔的禁用技能名列表。 |
| `REASONING_EFFORT` | `medium` | 推理强度:`low``medium``high``xhigh`。 |
| `SHOW_THINKING` | `True` | 是否显示思考过程。 |
| `INFINITE_SESSION` | `True` | 是否启用无限会话与上下文压缩。 |
| `MAX_MULTIPLIER` | `1.0` | 允许的最大账单倍率。`0` 表示仅允许免费模型。 |
| `EXCLUDE_KEYWORDS` | `""` | 排除包含这些关键词的模型。 |
| `TIMEOUT` | `300` | 每个流式分片的超时时间(秒)。 |
| `BYOK_TYPE` | `openai` | BYOK 提供商类型:`openai``anthropic`。 |
| `BYOK_BASE_URL` | `""` | BYOK Base URL。 |
| `BYOK_MODELS` | `""` | BYOK 模型列表,留空则尝试从 API 获取。 |
| `CUSTOM_ENV_VARS` | `""` | 自定义环境变量JSON 格式)。 |
| `DEBUG` | `False` | 启用浏览器控制台/技术调试日志。 |
### 2. 用户设置(个人覆盖)
普通用户可在个人资料或函数设置中覆盖以下选项。
| Valve | 描述 |
| :--- | :--- |
| `GH_TOKEN` | 使用个人 GitHub Token。 |
| `REASONING_EFFORT` | 个人推理强度偏好。 |
| `SHOW_THINKING` | 是否显示思考过程。 |
| `MAX_MULTIPLIER` | 个人最大账单倍率限制。 |
| `EXCLUDE_KEYWORDS` | 个人模型排除关键词。 |
| `ENABLE_OPENWEBUI_TOOLS` | 是否启用 OpenWebUI Tools 与 Built-in Tools。 |
| `ENABLE_OPENAPI_SERVER` | 是否启用 OpenAPI Tool Server。 |
| `ENABLE_MCP_SERVER` | 是否启用 MCP Server。 |
| `ENABLE_OPENWEBUI_SKILLS` | 是否加载你可读的 OpenWebUI Skills 到 SDK 技能目录。 |
| `DISABLED_SKILLS` | 逗号分隔的个人禁用技能列表。 |
| `BYOK_API_KEY` | 个人 BYOK API Key。 |
| `BYOK_TYPE` | 个人 BYOK 提供商类型覆盖。 |
| `BYOK_BASE_URL` | 个人 BYOK Base URL 覆盖。 |
| `BYOK_BEARER_TOKEN` | 个人 BYOK Bearer Token 覆盖。 |
| `BYOK_MODELS` | 个人 BYOK 模型列表覆盖。 |
| `BYOK_WIRE_API` | 个人 BYOK Wire API 覆盖。 |
---
## ⚙️ 配置参数 (Configuration Valves)
## 🚀 安装与配置
| 参数 | 默认值 | 描述 |
| :--- | :--- | :--- |
| `github_token` | - | GitHub Copilot 官方 Token (如果您有官方订阅且不方便本地登录时填入)。 |
| `llm_base_url` | - | BYOK 模式的基础 URL。填入后将绕过 GitHub 官方服务。 |
| `llm_api_key` | - | BYOK 模式的 API 密钥。 |
| `llm_model_id` | `gpt-4o` | 使用的模型 ID (官方、BYOK 均适用)。 |
| `workspace_root` | `./copilot_workspaces` | 所有会话沙盒的根目录。 |
| `skills_directory` | `./copilot_skills` | 自定义 SDK 技能文件夹所在的目录。 |
| `show_status` | `True` | 是否在 UI 显示 Agent 的实时运行状态和思考过程。 |
| `enable_infinite_session` | `True` | 是否开启自动上下文压缩和 TODO 列表持久化。 |
| `enable_html_artifacts` | `True` | 是否允许 Agent 生成并实时预览 HTML 应用。 |
| `enable_rich_ui` | `True` | 是否启用进度条和增强型工具调用面板。 |
### 1. 导入函数
1. 打开 OpenWebUI进入 **Workspace** -> **Functions**
2. 点击 **+**Create Function粘贴 `github_copilot_sdk.py` 内容。
3. 保存并确保已启用。
### 2. 获取 Token
1. 访问 [GitHub Token Settings](https://github.com/settings/tokens?type=beta)。
2. 创建 **Fine-grained token**,授予 **Account permissions** -> **Copilot Requests** 权限。
3. 将生成的 Token 填入 `GH_TOKEN`
### 3. 认证要求(必填其一)
必须至少配置一种凭据来源:
- `GH_TOKEN`GitHub Copilot 官方订阅路线),或
- `BYOK_API_KEY`OpenAI / Anthropic 自带 Key 路线)。
如果两者都未配置,模型列表将不会显示。
---
@@ -104,7 +155,13 @@
## ⚠️ 故障排除 (Troubleshooting)
- **工具无法使用?** 请检查是否安装了 `github-copilot-sdk`
- **文件找不到?** 确保已启用配套的 `Files Filter` 插件。
- **BYOK 报错?** 确认 `llm_base_url` 包含协议前缀(如 `https://`)且模型 ID 准确无误。
- **卡在 "Thinking..."?** 检查后端网络连接,流式传输可能受某些代理拦截。
- **工具无法使用?** 请先确认 OpenWebUI Tools / MCP / OpenAPI Server 已在对应设置中启用
- **文件找不到?** 确保已启用配套的 `Files Filter` 插件,否则 RAG 可能会提前消费原始文件
- **BYOK 报错?** 确认 `BYOK_BASE_URL` 包含正确协议前缀(如 `https://`且模型 ID 准确无误。
- **卡在 "Thinking..."?** 检查后端网络连接,或打开 `DEBUG` 查看更详细的 SDK 日志。
---
## Changelog
完整历史请查看 GitHub 项目主页:[OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions)

View File

@@ -15,7 +15,7 @@ Pipes allow you to:
## Available Pipe Plugins
- [GitHub Copilot SDK](github-copilot-sdk.md) (v0.9.1) - Official GitHub Copilot SDK integration. Features **Workspace Isolation**, **Zero-config OpenWebUI Tool Bridge**, **BYOK** support, and **dynamic MCP discovery**. **NEW in v0.9.1: MCP filter reliability fix** for `server:mcp:{id}` chat selection and function filter consistency. [View Deep Dive](github-copilot-sdk-deep-dive.md) | [**View Advanced Tutorial**](github-copilot-sdk-tutorial.md) | [**View Detailed Usage Guide**](github-copilot-sdk-usage-guide.md).
- [GitHub Copilot SDK](github-copilot-sdk.md) (v0.10.0) - Official GitHub Copilot SDK integration. Features **Workspace Isolation**, **Zero-config OpenWebUI Tool Bridge**, **BYOK** support, and **dynamic MCP discovery**. **NEW in v0.10.0: Native Prompt Restoration (Plan Mode & SQLite session management), Live TODO Widget integration, and SDK v0.1.30 alignment**. [View Deep Dive](github-copilot-sdk-deep-dive.md) | [**View Advanced Tutorial**](github-copilot-sdk-tutorial.md) | [**View Detailed Usage Guide**](github-copilot-sdk-usage-guide.md).
- **[Case Study: GitHub 100 Star Growth Analysis](star-prediction-example.md)** - Learn how to use the GitHub Copilot SDK Pipe with Minimax 2.1 to automatically analyze CSV data and generate project growth reports.
- **[Case Study: High-Quality Video to GIF Conversion](video-processing-example.md)** - See how the model uses system-level FFmpeg to accelerate, scale, and optimize colors for screen recordings.

View File

@@ -15,7 +15,7 @@ Pipes 可以用于:
## 可用的 Pipe 插件
- [GitHub Copilot SDK](github-copilot-sdk.zh.md) (v0.9.1) - GitHub Copilot SDK 官方集成。具备**工作区安全隔离**、**零配置工具桥接**与**BYOK (自带 Key) 支持**。**v0.9.1 更新MCP 过滤可靠性修复**,修正 `server:mcp:{id}` 聊天选择匹配并提升函数过滤一致性。[查看深度架构解析](github-copilot-sdk-deep-dive.zh.md) | [**查看进阶实战教程**](github-copilot-sdk-tutorial.zh.md) | [**查看详细使用手册**](github-copilot-sdk-usage-guide.zh.md)。
- [GitHub Copilot SDK](github-copilot-sdk.zh.md) (v0.10.0) - GitHub Copilot SDK 官方集成。具备**工作区安全隔离**、**零配置工具桥接**与**BYOK (自带 Key) 支持**。**v0.10.0 更新:原生提示词恢复(原生计划模式与 SQLite 会话管理)、新增紧凑型 Live TODO 小组件,并对齐 SDK v0.1.30**。[查看深度架构解析](github-copilot-sdk-deep-dive.zh.md) | [**查看进阶实战教程**](github-copilot-sdk-tutorial.zh.md) | [**查看详细使用手册**](github-copilot-sdk-usage-guide.zh.md)。
- **[实战案例GitHub 100 Star 增长预测](star-prediction-example.zh.md)** - 展示如何使用 GitHub Copilot SDK Pipe 结合 Minimax 2.1 模型,自动编写脚本分析 CSV 数据并生成详细的项目增长报告。
- **[实战案例:视频高质量 GIF 转换与加速](video-processing-example.zh.md)** - 演示模型如何通过底层 FFmpeg 工具对录屏进行加速、缩放及双阶段色彩优化处理。

View File

@@ -4,4 +4,5 @@ OpenWebUI native Tool plugins that can be used across models.
## Available Tool Plugins
- [OpenWebUI Skills Manager Tool](openwebui-skills-manager-tool.md) (v0.2.1) - Simple native skill management (`list/show/install/create/update/delete`).
- [OpenWebUI Skills Manager Tool](openwebui-skills-manager-tool.md) (v0.3.0) - Simple native skill management (`list/show/install/create/update/delete`).
- [Smart Mind Map Tool](smart-mind-map-tool.md) (v1.0.0) - Intelligently analyzes text content and proactively generates interactive mind maps to help users structure and visualize knowledge.

View File

@@ -4,4 +4,5 @@
## 可用 Tool 插件
- [OpenWebUI Skills 管理工具](openwebui-skills-manager-tool.zh.md) (v0.2.1) - 简化技能管理(`list/show/install/create/update/delete`)。
- [OpenWebUI Skills 管理工具](openwebui-skills-manager-tool.zh.md) (v0.3.0) - 简化技能管理(`list/show/install/create/update/delete`)。
- [智能思维导图工具 (Smart Mind Map Tool)](smart-mind-map-tool.zh.md) (v1.0.0) - 智能分析文本内容并主动生成交互式思维导图,帮助用户结构化与可视化知识。

View File

@@ -1,6 +1,6 @@
# OpenWebUI Skills Manager Tool
**Author:** [Fu-Jie](https://github.com/Fu-Jie/openwebui-extensions) | **Version:** 0.2.1 | **Project:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions)
**Author:** [Fu-Jie](https://github.com/Fu-Jie/openwebui-extensions) | **Version:** 0.3.0 | **Project:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions)
A standalone OpenWebUI Tool plugin for managing native Workspace Skills across models.

View File

@@ -1,6 +1,6 @@
# OpenWebUI Skills 管理工具
**Author:** [Fu-Jie](https://github.com/Fu-Jie/openwebui-extensions) | **Version:** 0.2.1 | **Project:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions)
**Author:** [Fu-Jie](https://github.com/Fu-Jie/openwebui-extensions) | **Version:** 0.3.0 | **Project:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions)
一个可跨模型使用的 OpenWebUI 原生 Tool 插件,用于管理 Workspace Skills。

View File

@@ -0,0 +1,62 @@
# Smart Mind Map Tool - Knowledge Visualization & Structuring
Smart Mind Map Tool is the tool version of the popular Smart Mind Map plugin for OpenWebUI. It allows the model to proactively generate interactive mind maps during conversations by intelligently analyzing context and structuring knowledge into visual hierarchies.
> **Note**: Prefer the manual trigger button instead? Check out the [Smart Mind Map Action Version](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a) here.
**Author:** [Fu-Jie](https://github.com/Fu-Jie) | **Version:** 1.0.0 | **Project:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **License:** MIT
---
## 🚀 Why is there a Tool version?
1. **Powered by OpenWebUI 0.8.0 Rich UI**: Previous versions of OpenWebUI did not support embedding custom HTML/iframes directly into the chat stream. Starting with 0.8.0, the platform introduced full Rich UI rendering support for **both Actions and Tools**, unleashing interactive frontend possibilities.
2. **AI Autonomous Invocation (vs. Action)**: While an **Action** is passive and requires a manual button click from the user, the **Tool** version gives the model **autonomy**. The AI can analyze the conversational context and decide on its own exactly when generating a mind map would be most helpful, offering a true "smart assistant" experience.
It is perfect for:
- Summarizing complex discussions.
- Planning projects or outlining articles.
- Explaining hierarchical concepts.
## ✨ Key Features
-**Proactive Generation**: The AI triggers the tool automatically when it senses a need for structured visualization.
-**Full Context Awareness**: Supports aggregation of the entire conversation history to generate comprehensive knowledge maps.
-**Native Multi-language UI (i18n)**: Automatically detects and adapts to your browser/system language (en-US, zh-CN, ja-JP, etc.).
-**Premium UI/UX**: Matches the Action version with a compact toolbar, glassmorphism aesthetics, and professional borders.
-**Interactive Controls**: Zoom (In/Out/Reset), Level-based expansion (Default to Level 3), and Fullscreen mode.
-**High-Quality Export**: Export your mind maps as print-ready PNG images.
## 🛠️ Installation & Setup
1. **Install**: Upload `smart_mind_map_tool.py` to your OpenWebUI Admin Settings -> Plugins -> Tools.
2. **Enable Native Tool Calling**: Navigate to `Admin Settings -> Models` or your workspace settings, and ensure that **Native Tool Calling** is enabled for your selected model. This is required for the AI to reliably and actively invoke the tool automatically.
3. **Assign**: Toggle the tool "ON" for your desired models in the workspace or model settings.
4. **Configure**:
- `MESSAGE_COUNT`: Set to `12` (default) to use the 12 most recent messages, or `0` for the entire conversation history.
- `MODEL_ID`: Specify a preferred model for analysis (defaults to the current chat model).
## ⚙️ Configuration (Valves)
| Parameter | Default | Description |
| :--- | :--- | :--- |
| `MODEL_ID` | (Empty) | The model used for text analysis. If empty, uses the current chat model. |
| `MESSAGE_COUNT` | `12` | Number of messages to aggregate. `0` = All messages. |
| `MIN_TEXT_LENGTH` | `100` | Minimum character count required to trigger a mind map. |
## ❓ FAQ & Troubleshooting
- **Language mismatch?**: The tool uses a 4-level detection (Frontend Script > Browser Header > User Profile > Default). Ensure your browser language is set correctly.
- **Too tiny or too large?**: We've optimized the height to `500px` for inline chat display with a responsive "Fit to Screen" logic.
- **Exporting**: Click the "⛶" for fullscreen if you want a wider view before exporting to PNG.
---
## ⭐ Support
If this tool helps you visualize ideas better, please give us a star on [GitHub](https://github.com/Fu-Jie/openwebui-extensions).
## ⚖️ License
MIT License. Developed with ❤️ by Fu-Jie.

View File

@@ -0,0 +1,62 @@
# 思维导图工具 - 知识可视化与结构化利器
思维导图工具Smart Mind Map Tool是广受好评的“思维导图”插件的工具Tool版本。它赋予了模型主动生成交互式思维导图的能力通过智能分析上下文将碎片化知识转化为层级分明的视觉架构。
> **说明**:如果您更倾向于手动点击按钮触发生成,可以获取 [思维导图 Action动作版本](https://openwebui.com/posts/turn_any_text_into_beautiful_mind_maps_3094c59a)。
**作者:** [Fu-Jie](https://github.com/Fu-Jie) | **版本:** 1.0.0 | **项目:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **许可证:** MIT
---
## 🚀 为什么会有工具Tool版本
1. **得益于 OpenWebUI 0.8.0 的 Rich UI 特性**:在以前的版本中,是不支持直接将自定义的 HTML/iframe 嵌入到对话流中的。而从 0.8.0 开始平台不仅支持了这种顺滑的前端组件直出Rich UI而且同时对 **Action****Tool** 开放了该能力。
2. **AI 自主调用(区别于 Action****Action** 是被动的,需要用户在输入框或消息旁手动点击触发;而 **Tool** 赋予了模型**自主权**。AI 可以根据对话上下文,自行判断在什么时候为您生成导图最有帮助,实现真正的“智能助理”体验。
它非常适合以下场景:
- 总结复杂的对话内容。
- 规划项目、整理文章大纲。
- 解释具有层级结构的抽象概念。
## ✨ 核心特性
-**主动触发生成**AI 在感知到需要视觉化展示时会自动调用工具生成导图。
-**全量上下文感知**支持聚合整个会话历史MESSAGE_COUNT 为 0生成最完整的知识地图。
-**原生多语言 UI (i18n)**:自动检测并适配浏览器/系统语言(简体中文、繁体中文、英文、日文、韩文等)。
-**统一的高级视觉**:完全复刻 Action 版本的极简工具栏、玻璃拟态审美以及专业边框阴影。
-**深度交互控制**:支持缩放(放大/缩小/重置)、层级调节(默认为 3 级展开)以及全屏模式。
-**高品质导出**:支持将导图导出为超高清 PNG 图片。
## 🛠️ 安装与设置
1. **安装**:在 OpenWebUI 管理员设置 -> 插件 -> 工具中上传 `smart_mind_map_tool.py`
2. **启用原生理机制**:在“管理员设置 -> 模型”或配置里,确保目标模型**启用了原生工具调用Native Tool Calling**。只有开启这个能力AI 才能自主并稳定地触发 Tool 功能。
3. **分配工具**:在工作区或聊天界面处为目标模型选中并挂载本工具。
4. **配置**
- `MESSAGE_COUNT`:设置为 `12`(默认)以使用最近的 12 条对话记录,或设置为 `0` 聚合全部历史。
- `MODEL_ID`:指定分析导图时偏好的模型(留空则默认使用当前模型)。
## ⚙️ 配置参数 (Valves)
| 参数 | 默认值 | 描述 |
| :--- | :--- | :--- |
| `MODEL_ID` | (留空) | 用于文本分析的模型 ID。留空则随当前聊天模型。 |
| `MESSAGE_COUNT` | `12` | 聚合消息的数量。`0` 表示全量消息,`12` 表示截取最近的 12 条。 |
| `MIN_TEXT_LENGTH` | `100` | 触发导图分析所需的最小字符长度。 |
## ❓ 常见问题
- **语言显示不正确?**:工具采用 4 级探测机制(前端脚本 > 浏览器头 > 用户资料 > 默认)。请检查浏览器语言设置。
- **生成的导图太小或太大?**:我们针对对话流内联显示优化了 `500px` 的固定高度,并配有自适应缩放逻辑。
- **导出图片**:建议先点击“⛶”进入全屏,获得最佳构图后再点击导出。
---
## ⭐ 支持
如果这个工具帮您理清了思路,欢迎在 [GitHub](https://github.com/Fu-Jie/openwebui-extensions) 给我们一个 Star。
## ⚖️ 许可证
MIT License. Designed with ❤️ by Fu-Jie.

51
original_system_prompt.md Normal file
View File

@@ -0,0 +1,51 @@
You are a helpful assistant.
[Session Context]
- **Your Isolated Workspace**: `/app/backend/data/copilot_workspace/user_123/chat_456`
- **Active User ID**: `user_123`
- **Active Chat ID**: `chat_456`
- **Skills Directory**: `/app/backend/data/skills/shared/` — contains user-installed skills.
- **Config Directory**: `/app/backend/data/.copilot` — system configuration (Restricted).
- **CLI Tools Path**: `/app/backend/data/.copilot_tools/` — Global tools installed via npm or pip will automatically go here and be in your $PATH. Python tools are strictly isolated in a venv here.
**CRITICAL INSTRUCTION**: You MUST use the above workspace for ALL file operations.
- DO NOT create files in `/tmp` or any other system directories.
- Always interpret 'current directory' as your Isolated Workspace.
[Available Native System Tools]
The host environment is rich. Based on the official OpenWebUI Docker deployment baseline (backend image), the following CLI tools are expected to be preinstalled and globally available in $PATH:
- **Network/Data**: `curl`, `jq`, `netcat-openbsd`
- **Media/Doc**: `pandoc` (format conversion), `ffmpeg` (audio/video)
- **Build/System**: `git`, `gcc`, `make`, `build-essential`, `zstd`, `bash`
- **Python/Runtime**: `python3`, `pip3`, `uv`
- **Verification Rule**: Before installing any CLI/tool dependency, first check availability with `which <tool>` or a lightweight version probe (e.g. `<tool> --version`).
- **Python Libs**: The active virtual environment inherits `--system-site-packages`. Advanced libraries like `pandas`, `numpy`, `pillow`, `opencv-python-headless`, `pypdf`, `langchain`, `playwright`, `httpx`, and `beautifulsoup4` are ALREADY installed. Try importing them before attempting to install.
[Mode Context: Plan Mode]
You are currently operating in **Plan Mode**.
DEFINITION: Plan mode is a collaborative phase to outline multi-step plans or conduct research BEFORE any code is modified.
<workflow>
1. Clarification: If requirements/goals are ambiguous, ask questions.
2. Analysis: Analyze the codebase to understand constraints. You MAY use shell commands (e.g., `ls`, `grep`, `find`, `cat`) and other read-only tools.
3. Formulation: Generate your structured plan OR research findings.
4. Approval: Present the detailed plan directly to the user for approval via chat.
</workflow>
<key_principles>
- ZERO CODE MODIFICATION: You must NOT execute file edits, write operations, or destructive system changes. Your permissions are locked to READ/RESEARCH ONLY, with the sole exception of the progress-tracking file `plan.md`.
- SHELL USAGE: Shell execution is ENABLED for research purposes. Any attempts to modify the filesystem via shell (e.g., `sed -i`, `rm`) will be strictly blocked, except for appending to `plan.md`.
- PURE RESEARCH SUPPORT: If the user requests a pure research report, output your conclusions directly matching the plan style.
- PERSISTENCE: You MUST save your proposed plan to `/app/backend/data/.copilot/session-state/chat_456/plan.md` to sync with the UI. The UI automatically reads this file to update the plan view.
</key_principles>
<plan_format>
When presenting your findings or plan in the chat, structure it clearly:
## Plan / Report: {Title}
**TL;DR**: {Summary}
**Detailed Tasks / Steps**: {List step-by-step}
**Affected Files**:
- `path/to/file`
**Constraint/Status**: {Any constraints}
</plan_format>
Acknowledge your role as a planner and format your next response using the plan style above.

View File

@@ -0,0 +1,206 @@
# BYOK模式与Infinite Session(自动上下文压缩)兼容性研究
**日期**: 2026-03-08
**研究范围**: Copilot SDK v0.1.30 + OpenWebUI Extensions Pipe v0.10.0
## 研究问题
在BYOK (Bring Your Own Key) 模式下,是否应该支持自动上下文压缩(Infinite Sessions)?
用户报告BYOK模式本不应该触发压缩但当模型名称与Copilot内置模型一致时意外地支持了压缩。
---
## 核心发现
### 1. SDK层面copilot-sdk/python/copilot/types.py
**InfiniteSessionConfig 定义** (line 453-470):
```python
class InfiniteSessionConfig(TypedDict, total=False):
"""
Configuration for infinite sessions with automatic context compaction
and workspace persistence.
"""
enabled: bool
background_compaction_threshold: float # 0.0-1.0, default: 0.80
buffer_exhaustion_threshold: float # 0.0-1.0, default: 0.95
```
**SessionConfig结构** (line 475+):
- `provider: ProviderConfig` - 用于BYOK配置
- `infinite_sessions: InfiniteSessionConfig` - 上下文压缩配置
- **关键**: 这两个配置是**完全独立的**,没有相互依赖关系
### 2. OpenWebUI Pipe层面github_copilot_sdk.py
**Infinite Session初始化** (line 5063-5069):
```python
infinite_session_config = None
if self.valves.INFINITE_SESSION: # 默认值: True
infinite_session_config = InfiniteSessionConfig(
enabled=True,
background_compaction_threshold=self.valves.COMPACTION_THRESHOLD,
buffer_exhaustion_threshold=self.valves.BUFFER_THRESHOLD,
)
```
**关键问题**:
- ✗ 没有任何条件检查 `is_byok_model`
- ✗ 无论使用官方模型还是BYOK模型都会应用相同的infinite session配置
- ✓ 回对比reasoning_effort被正确地在BYOK模式下禁用line 6329-6331
### 3. 模型识别逻辑line 6199+
```python
if m_info and "source" in m_info:
is_byok_model = m_info["source"] == "byok"
else:
is_byok_model = not has_multiplier and byok_active
```
BYOK模型识别基于:
1. 模型元数据中的 `source` 字段
2. 或者根据是否有乘数标签 (如 "4x", "0.5x") 和globally active的BYOK配置
---
## 技术可行性分析
### ✅ Infinite Sessions在BYOK模式下是技术可行的
1. **SDK支持**: Copilot SDK允许在任何provider (官方、BYOK、Azure等) 下使用infinite session配置
2. **配置独立性**: provider和infinite_sessions配置在SessionConfig中是独立的字段
3. **无文档限制**: SDK文档中没有说BYOK模式不支持infinite sessions
4. **测试覆盖**: SDK虽然有单独的BYOK测试和infinite-sessions测试但缺少组合测试
### ⚠️ 但存在以下设计问题:
#### 问题1: 意外的自动启用
- BYOK模式通常用于**精确控制**自己的API使用
- 自动压缩可能会导致**意外的额外请求**和API成本增加
- 没有明确的警告或文档说明BYOK也会压缩
#### 问题2: 没有模式特定的配置
```python
# 当前实现 - 一刀切
if self.valves.INFINITE_SESSION:
# 同时应用于官方模型和BYOK模型
# 应该是 - 模式感知
if self.valves.INFINITE_SESSION and not is_byok_model:
# 仅对官方模型启用
# 或者
if self.valves.INFINITE_SESSION_BYOK and is_byok_model:
# BYOK专用配置
```
#### 问题3: 压缩质量不确定性
- BYOK模型可能是自部署的或开源模型
- 上下文压缩由Copilot CLI处理质量取决于CLI版本
- 没有标准化的压缩效果评估
---
## 用户报告现象的根本原因
用户说:"BYOK模式本不应该触发压缩但碰巧用的模型名称与Copilot内置模型相同结果意外触发了压缩"
**分析**:
1. OpenWebUI Pipe中infinite_session配置是**全局启用**的 (INFINITE_SESSION=True)
2. 模型识别逻辑中如果模型元数据丢失会根据模型名称和BYOK活跃状态来推断
3. 如果用户使用的BYOK模型名称恰好是 "gpt-4", "claude-3-5-sonnet" 等,可能被识别错误
4. 或者用户根本没意识到infinite session在BYOK模式下也被启用了
---
## 建议方案
### 方案1: 保守方案(推荐)
**禁用BYOK模式下的automatic compression**
```python
infinite_session_config = None
# 只对标准官方模型启用不对BYOK启用
if self.valves.INFINITE_SESSION and not is_byok_model:
infinite_session_config = InfiniteSessionConfig(
enabled=True,
background_compaction_threshold=self.valves.COMPACTION_THRESHOLD,
buffer_exhaustion_threshold=self.valves.BUFFER_THRESHOLD,
)
```
**优点**:
- 尊重BYOK用户的成本控制意愿
- 降低意外API使用风险
- 与reasoning_effort的BYOK禁用保持一致
**缺点**: 限制了BYOK用户的功能
### 方案2: 灵活方案
**添加独立的BYOK compression配置**
```python
class Valves(BaseModel):
INFINITE_SESSION: bool = Field(
default=True,
description="Enable Infinite Sessions for standard Copilot models"
)
INFINITE_SESSION_BYOK: bool = Field(
default=False,
description="Enable Infinite Sessions for BYOK models (advanced users only)"
)
# 使用逻辑
if (self.valves.INFINITE_SESSION and not is_byok_model) or \
(self.valves.INFINITE_SESSION_BYOK and is_byok_model):
infinite_session_config = InfiniteSessionConfig(...)
```
**优点**:
- 给BYOK用户完全控制
- 保持向后兼容性
- 允许高级用户启用
**缺点**: 增加配置复杂度
### 方案3: 警告+ 文档
**保持当前实现,但添加文档说明**
- 在README中明确说明infinite session对所有provider类型都启用
- 添加Valve描述提示: "Applies to both standard Copilot and BYOK models"
- 在BYOK配置部分明确提到压缩成本
**优点**: 减少实现负担,给用户知情权
**缺点**: 对已经启用的用户无帮助
---
## 推荐实施
**优先级**: 高
**建议实施方案**: **方案1 (保守方案)****方案2 (灵活方案)**
如果选择方案1: 修改line 5063处的条件判断
如果选择方案2: 添加INFINITE_SESSION_BYOK配置 + 修改初始化逻辑
---
## 相关代码位置
| 文件 | 行号 | 说明 |
|-----|------|------|
| `github_copilot_sdk.py` | 364-366 | INFINITE_SESSION Valve定义 |
| `github_copilot_sdk.py` | 5063-5069 | Infinite session初始化 |
| `github_copilot_sdk.py` | 6199-6220 | is_byok_model判断逻辑 |
| `github_copilot_sdk.py` | 6329-6331 | reasoning_effort BYOK处理参考 |
---
## 结论
**BYOK模式与Infinite Sessions的兼容性**:
- ✅ 技术上完全可行
- ⚠️ 但存在设计意图不清的问题
- ✗ 当前实现对BYOK用户可能不友好
**推荐**: 实施方案1或2之一增加BYOK模式的控制粒度。

View File

@@ -0,0 +1,295 @@
# Client传入和管理分析
## 当前的Client管理架构
```
┌────────────────────────────────────────┐
│ Pipe Instance (github_copilot_sdk.py) │
│ │
│ _shared_clients = { │
│ "token_hash_1": CopilotClient(...), │ ← 基于GitHub Token缓存
│ "token_hash_2": CopilotClient(...), │
│ } │
└────────────────────────────────────────┘
│ await _get_client(token)
┌────────────────────────────────────────┐
│ CopilotClient Instance │
│ │
│ [仅需GitHub Token配置] │
│ │
│ config { │
│ github_token: "ghp_...", │
│ cli_path: "...", │
│ config_dir: "...", │
│ env: {...}, │
│ cwd: "..." │
│ } │
└────────────────────────────────────────┘
│ create_session(session_config)
┌────────────────────────────────────────┐
│ Session (per-session configuration) │
│ │
│ session_config { │
│ model: "real_model_id", │
│ provider: { │ ← ⭐ BYOK配置在这里
│ type: "openai", │
│ base_url: "https://api.openai...",
│ api_key: "sk-...", │
│ ... │
│ }, │
│ infinite_sessions: {...}, │
│ system_message: {...}, │
│ ... │
│ } │
└────────────────────────────────────────┘
```
---
## 目前的流程(代码实际位置)
### 步骤1获取或创建Clientline 6208
```python
# _pipe_impl中
client = await self._get_client(token)
```
### 步骤2_get_client函数line 5523-5561
```python
async def _get_client(self, token: str) -> Any:
"""Get or create the persistent CopilotClient from the pool based on token."""
if not token:
raise ValueError("GitHub Token is required to initialize CopilotClient")
token_hash = hashlib.md5(token.encode()).hexdigest()
# 查看是否已有缓存的client
client = self.__class__._shared_clients.get(token_hash)
if client and client状态正常:
return client # ← 复用已有的client
# 否则创建新client
client_config = self._build_client_config(user_id=None, chat_id=None)
client_config["github_token"] = token
new_client = CopilotClient(client_config)
await new_client.start()
self.__class__._shared_clients[token_hash] = new_client
return new_client
```
### 步骤3创建会话时传入providerline 6253-6270
```python
# _pipe_impl中BYOK部分
if is_byok_model:
provider_config = {
"type": byok_type, # "openai" or "anthropic"
"wire_api": byok_wire_api,
"base_url": byok_base_url,
"api_key": byok_api_key or None,
"bearer_token": byok_bearer_token or None,
}
# 然后传入session config
session = await client.create_session(config={
"model": real_model_id,
"provider": provider_config, # ← provider在这里传给session
...
})
```
---
## 关键问题架构的2个层级
| 层级 | 用途 | 配置内容 | 缓存方式 |
|------|------|---------|---------|
| **CopilotClient** | CLI和运行时底层逻辑 | GitHub Token, CLI path, 环境变量 | 基于token_hash全局缓存 |
| **Session** | 具体的对话会话 | Model, Provider(BYOK), Tools, System Prompt | 不缓存(每次新建) |
---
## 当前的问题
### 问题1Client是全局缓存的但Provider是会话级别的
```python
# ❓ 如果用户想为不同的BYOK模型使用不同的Client呢
# 当前无法做到因为Client基于token缓存是全局的
# 例子:
# Client A: OpenAI API key (token_hash_1)
# Client B: Anthropic API key (token_hash_2)
# 但在Pipe中只有一个GH_TOKEN导致只能有一个Client
```
### 问题2Provider和Client是不同的东西
```python
# CopilotClient = GitHub Copilot SDK客户端
# ProviderConfig = OpenAI/Anthropic等的API配置
# 用户可能混淆:
# "怎么传入BYOK的client和provider"
# → 实际上只能传provider到sessionclient是全局的
```
### 问题3BYOK模型混用的情况处理不清楚
```python
# 如果用户想在同一个Pipe中
# - Model A 用 OpenAI API
# - Model B 用 Anthropic API
# - Model C 用自己的本地LLM
# 当前代码是基于全局BYOK配置的无法为各模型单独设置
```
---
## 改进方案
### 方案A保持当前架构只改Provider映射
**思路**Client保持全局基于GH_TOKEN但Provider配置基于模型动态选择
```python
# 在Valves中添加
class Valves(BaseModel):
# ... 现有配置 ...
# 新增模型到Provider的映射 (JSON)
MODEL_PROVIDER_MAP: str = Field(
default="{}",
description='Map model IDs to BYOK providers (JSON). Example: '
'{"gpt-4": {"type": "openai", "base_url": "...", "api_key": "..."}, '
'"claude-3": {"type": "anthropic", "base_url": "...", "api_key": "..."}}'
)
# 在_pipe_impl中
def _get_provider_config(self, model_id: str, byok_active: bool) -> Optional[dict]:
"""Get provider config for a specific model"""
if not byok_active:
return None
try:
model_map = json.loads(self.valves.MODEL_PROVIDER_MAP or "{}")
return model_map.get(model_id)
except:
return None
# 使用时
provider_config = self._get_provider_config(real_model_id, byok_active) or {
"type": byok_type,
"base_url": byok_base_url,
"api_key": byok_api_key,
...
}
```
**优点**最小改动复用现有Client架构
**缺点**多个BYOK模型仍共享一个Client只要GH_TOKEN相同
---
### 方案B为不同BYOK提供商创建不同的Client
**思路**扩展_get_client支持基于provider_type的多client缓存
```python
async def _get_or_create_client(
self,
token: str,
provider_type: str = "github" # "github", "openai", "anthropic"
) -> Any:
"""Get or create client based on token and provider type"""
if provider_type == "github" or not provider_type:
# 现有逻辑
token_hash = hashlib.md5(token.encode()).hexdigest()
else:
# 为BYOK提供商创建不同的client
composite_key = f"{token}:{provider_type}"
token_hash = hashlib.md5(composite_key.encode()).hexdigest()
# 从缓存获取或创建
...
```
**优点**隔离不同BYOK提供商的Client
**缺点**:更复杂,需要更多改动
---
## 建议的改进路线
**优先级1方案A - 模型到Provider的映射**
添加Valves配置
```python
MODEL_PROVIDER_MAP: str = Field(
default="{}",
description='Map specific models to their BYOK providers (JSON format)'
)
```
使用方式:
```
{
"gpt-4": {
"type": "openai",
"base_url": "https://api.openai.com/v1",
"api_key": "sk-..."
},
"claude-3": {
"type": "anthropic",
"base_url": "https://api.anthropic.com/v1",
"api_key": "ant-..."
},
"llama-2": {
"type": "openai", # 开源模型通常使用openai兼容API
"base_url": "http://localhost:8000/v1",
"api_key": "sk-local"
}
}
```
**优先级2在_build_session_config中考虑provider_config**
修改infinite_session初始化基于provider_config判断
```python
def _build_session_config(..., provider_config=None):
# 如果使用了BYOK provider需要特殊处理infinite_session
infinite_session_config = None
if self.valves.INFINITE_SESSION and provider_config is None:
# 仅官方Copilot模型启用compression
infinite_session_config = InfiniteSessionConfig(...)
```
**优先级3方案B - 多client缓存长期改进**
如果需要完全隔离不同BYOK提供商的Client。
---
## 总结如果你要传入BYOK client
**现状**
- CopilotClient是基于GH_TOKEN全局缓存的
- Provider配置是在SessionConfig级别动态设置的
- 一个Client可以创建多个Session每个Session用不同的Provider
**改进后**
- 添加MODEL_PROVIDER_MAP配置
- 对每个模型的请求动态选择对应的Provider配置
- 同一个Client可以为不同Provider服务不同的models
**你需要做的**
1. 在Valves中配置MODEL_PROVIDER_MAP
2. 在模型选择时读取这个映射
3. 创建session时用对应的provider_config
无需修改Client的创建逻辑

View File

@@ -0,0 +1,324 @@
# 数据流分析SDK如何获知用户设计的数据
## 当前数据流从OpenWebUI → Pipe → SDK
```
┌─────────────────────┐
│ OpenWebUI UI │
│ (用户选择模型) │
└──────────┬──────────┘
├─ body.model = "gpt-4"
├─ body.messages = [...]
├─ __metadata__.base_model_id = ?
├─ __metadata__.custom_fields = ?
└─ __user__.settings = ?
┌──────────▼──────────┐
│ Pipe (github- │
│ copilot-sdk.py) │
│ │
│ 1. 提取model信息 │
│ 2. 应用Valves配置 │
│ 3. 建立SDK会话 │
└──────────┬──────────┘
├─ SessionConfig {
│ model: real_model_id
│ provider: ProviderConfig (若BYOK)
│ infinite_sessions: {...}
│ system_message: {...}
│ ...
│ }
┌──────────▼──────────┐
│ Copilot SDK │
│ (create_session) │
│ │
│ 返回:ModelInfo { │
│ capabilities { │
│ limits { │
│ max_context_ │
│ window_tokens │
│ } │
│ } │
│ } │
└─────────────────────┘
```
---
## 关键问题当前的3个瓶颈
### 瓶颈1用户数据的输入点
**当前支持的输入方式:**
1. **Valves配置全局 + 用户级)**
```python
# 全局设置Admin
Valves.BYOK_BASE_URL = "https://api.openai.com/v1"
Valves.BYOK_API_KEY = "sk-..."
# 用户级覆盖
UserValves.BYOK_API_KEY = "sk-..." (用户自己的key)
UserValves.BYOK_BASE_URL = "..."
```
**问题**无法为特定的BYOK模型设置上下文窗口大小
2. **__metadata__来自OpenWebUI**
```python
__metadata__ = {
"base_model_id": "...",
"custom_fields": {...}, # ← 可能包含额外信息
"tool_ids": [...],
}
```
**问题**不清楚OpenWebUI是否支持通过metadata传递模型的上下文窗口
3. **body来自对话请求**
```python
body = {
"model": "gpt-4",
"messages": [...],
"temperature": 0.7,
# ← 这里能否添加自定义字段?
}
```
---
### 瓶颈2模型信息的识别和存储
**当前代码** (line 5905+)
```python
# 解析用户选择的模型
request_model = body.get("model", "") # e.g., "gpt-4"
real_model_id = request_model
# 确定实际模型ID
base_model_id = _container_get(__metadata__, "base_model_id", "")
if base_model_id:
resolved_id = base_model_id # 使用元数据中的ID
else:
resolved_id = request_model # 使用用户选择的ID
```
**问题**
- ❌ 没有维护一个"模型元数据缓存"
- ❌ 对相同模型的重复请求,每次都需要重新识别
- ❌ 不能为特定模型持久化上下文窗口大小
---
### 瓶颈3SDK会话配置的构建
**当前实现** (line 5058-5100)
```python
def _build_session_config(
self,
real_model_id, # ← 模型ID
system_prompt_content,
is_streaming=True,
is_admin=False,
# ... 其他参数
):
# 无条件地创建infinite session
if self.valves.INFINITE_SESSION:
infinite_session_config = InfiniteSessionConfig(
enabled=True,
background_compaction_threshold=self.valves.COMPACTION_THRESHOLD, # 0.80
buffer_exhaustion_threshold=self.valves.BUFFER_THRESHOLD, # 0.95
)
# ❌ 这里没有查询该模型的实际上下文窗口大小
# ❌ 无法根据模型的真实限制调整压缩阈值
```
---
## 解决方案3个数据流改进步骤
### 步骤1添加模型元数据配置优先级
在Valves中添加一个**模型元数据映射**
```python
class Valves(BaseModel):
# ... 现有配置 ...
# 新增:模型上下文窗口映射 (JSON格式)
MODEL_CONTEXT_WINDOWS: str = Field(
default="{}", # JSON string
description='Model context window mapping (JSON). Example: {"gpt-4": 8192, "gpt-4-turbo": 128000, "claude-3": 200000}'
)
# 新增BYOK模型特定设置 (JSON格式)
BYOK_MODEL_CONFIG: str = Field(
default="{}", # JSON string
description='BYOK-specific model configuration (JSON). Example: {"gpt-4": {"context_window": 8192, "enable_compression": true}}'
)
```
**如何使用**
```python
# Valves中设置
MODEL_CONTEXT_WINDOWS = '{"gpt-4": 8192, "claude-3-5-sonnet": 200000}'
# Pipe中解析
def _get_model_context_window(self, model_id: str) -> Optional[int]:
"""从配置中获取模型的上下文窗口大小"""
try:
config = json.loads(self.valves.MODEL_CONTEXT_WINDOWS or "{}")
return config.get(model_id)
except:
return None
```
### 步骤2建立模型信息缓存优先级
在Pipe中维护一个模型信息缓存
```python
class Pipe:
def __init__(self):
# ... 现有代码 ...
self._model_info_cache = {} # model_id -> ModelInfo
self._context_window_cache = {} # model_id -> context_window_tokens
def _cache_model_info(self, model_id: str, model_info: ModelInfo):
"""缓存SDK返回的模型信息"""
self._model_info_cache[model_id] = model_info
if model_info.capabilities and model_info.capabilities.limits:
self._context_window_cache[model_id] = (
model_info.capabilities.limits.max_context_window_tokens
)
def _get_context_window(self, model_id: str) -> Optional[int]:
"""获取模型的上下文窗口大小优先级SDK > Valves配置 > 默认值)"""
# 1. 优先从SDK缓存获取最可靠
if model_id in self._context_window_cache:
return self._context_window_cache[model_id]
# 2. 其次从Valves配置获取
context_window = self._get_model_context_window(model_id)
if context_window:
return context_window
# 3. 默认值(未知)
return None
```
### 步骤3使用真实的上下文窗口来优化压缩策略优先级
修改_build_session_config
```python
def _build_session_config(
self,
real_model_id,
# ... 其他参数 ...
**kwargs
):
# 获取模型的真实上下文窗口大小
actual_context_window = self._get_context_window(real_model_id)
# 只对有明确上下文窗口的模型启用压缩
infinite_session_config = None
if self.valves.INFINITE_SESSION and actual_context_window:
# 现在压缩阈值有了明确的含义
infinite_session_config = InfiniteSessionConfig(
enabled=True,
# 80% of actual context window
background_compaction_threshold=self.valves.COMPACTION_THRESHOLD,
# 95% of actual context window
buffer_exhaustion_threshold=self.valves.BUFFER_THRESHOLD,
)
await self._emit_debug_log(
f"Infinite Session: model_context={actual_context_window}tokens, "
f"compaction_triggers_at={int(actual_context_window * self.valves.COMPACTION_THRESHOLD)}, "
f"buffer_triggers_at={int(actual_context_window * self.valves.BUFFER_THRESHOLD)}",
__event_call__,
)
elif self.valves.INFINITE_SESSION and not actual_context_window:
logger.warning(
f"Infinite Session: Unknown context window for {real_model_id}, "
f"compression disabled. Set MODEL_CONTEXT_WINDOWS in Valves to enable."
)
```
---
## 具体的配置示例
### 例子1用户配置BYOK模型的上下文窗口
**Valves设置**
```
MODEL_CONTEXT_WINDOWS = {
"gpt-4": 8192,
"gpt-4-turbo": 128000,
"gpt-4o": 128000,
"claude-3": 200000,
"claude-3.5-sonnet": 200000,
"llama-2-70b": 4096
}
```
**效果**
- Pipe会知道"gpt-4"的上下文是8192 tokens
- 压缩会在 ~6553 tokens (80%) 时触发
- 缓冲会在 ~7782 tokens (95%) 时阻塞
### 例子2为特定BYOK模型启用/禁用压缩
**Valves设置**
```
BYOK_MODEL_CONFIG = {
"gpt-4": {
"context_window": 8192,
"enable_infinite_session": true,
"compaction_threshold": 0.75
},
"llama-2-70b": {
"context_window": 4096,
"enable_infinite_session": false # 禁用压缩
}
}
```
**Pipe逻辑**
```python
# 检查模型特定的压缩设置
def _get_compression_enabled(self, model_id: str) -> bool:
try:
config = json.loads(self.valves.BYOK_MODEL_CONFIG or "{}")
model_config = config.get(model_id, {})
return model_config.get("enable_infinite_session", self.valves.INFINITE_SESSION)
except:
return self.valves.INFINITE_SESSION
```
---
## 总结SDK如何获知用户设计的数据
| 来源 | 方式 | 更新 | 示例 |
|------|------|------|------|
| **Valves** | 全局配置 | Admin提前设置 | `MODEL_CONTEXT_WINDOWS` JSON |
| **SDK** | SessionConfig返回 | 每次会话创建 | `model_info.capabilities.limits` |
| **缓存** | Pipe本地存储 | 首次获取后缓存 | `_context_window_cache` |
| **__metadata__** | OpenWebUI传递 | 每次请求随带 | `base_model_id`, custom fields |
**流程**
1. 用户在Valves中配置 `MODEL_CONTEXT_WINDOWS`
2. Pipe在session创建时获取SDK返回的model_info
3. Pipe缓存上下文窗口大小
4. Pipe根据真实窗口大小调整infinite session的阈值
5. SDK使用正确的压缩策略
这样,**SDK完全知道用户设计的数据**而无需任何修改SDK本身。

View File

@@ -0,0 +1,163 @@
# SDK中的上下文限制信息
## SDK类型定义
### 1. ModelLimitscopilot-sdk/python/copilot/types.py, line 761-789
```python
@dataclass
class ModelLimits:
"""Model limits"""
max_prompt_tokens: int | None = None # 最大提示符tokens
max_context_window_tokens: int | None = None # 最大上下文窗口tokens
vision: ModelVisionLimits | None = None # 视觉相关限制
```
### 2. ModelCapabilitiesline 817-843
```python
@dataclass
class ModelCapabilities:
"""Model capabilities and limits"""
supports: ModelSupports # 支持的功能vision, reasoning_effort等
limits: ModelLimits # 上下文和token限制
```
### 3. ModelInfoline 889-949
```python
@dataclass
class ModelInfo:
"""Information about an available model"""
id: str
name: str
capabilities: ModelCapabilities # ← 包含limits信息
policy: ModelPolicy | None = None
billing: ModelBilling | None = None
supported_reasoning_efforts: list[str] | None = None
default_reasoning_effort: str | None = None
```
---
## 关键发现
### ✅ SDK提供的信息
- `model.capabilities.limits.max_context_window_tokens` - 模型的上下文窗口大小
- `model.capabilities.limits.max_prompt_tokens` - 最大提示符tokens
### ❌ OpenWebUI Pipe中的问题
**目前Pipe完全没有使用这些信息**
`github_copilot_sdk.py` 中搜索 `max_context_window`, `capabilities`, `limits` 等,结果为空。
---
## 这对BYOK意味着什么
### 问题1: BYOK模型的上下文限制未知
```python
# BYOK模型的capabilities来自哪里
if is_byok_model:
# ❓ BYOK模型没有能力信息返回吗
# ❓ 如何知道它的max_context_window_tokens
pass
```
### 问题2: Infinite Session的阈值是硬编码的
```python
COMPACTION_THRESHOLD: float = Field(
default=0.80, # 80%时触发后台压缩
description="Background compaction threshold (0.0-1.0)"
)
BUFFER_THRESHOLD: float = Field(
default=0.95, # 95%时阻塞直到压缩完成
description="Buffer exhaustion threshold (0.0-1.0)"
)
# 但是 0.80 和 0.95 是什么的百分比?
# - 是模型的max_context_window_tokens吗
# - 还是固定的某个值?
# - BYOK模型的上下文窗口可能完全不同
```
---
## 改进方向
### 方案A: 利用SDK提供的模型限制信息
```python
# 在获取模型信息时保存capabilities
self._model_capabilities = model_info.capabilities
# 在初始化infinite session时使用实际的上下文窗口
if model_info.capabilities.limits.max_context_window_tokens:
actual_context_window = model_info.capabilities.limits.max_context_window_tokens
# 动态调整压缩阈值而不是固定值
compaction_threshold = self.valves.COMPACTION_THRESHOLD
buffer_threshold = self.valves.BUFFER_THRESHOLD
# 这些现在有了明确的含义:是模型实际上下文窗口大小的百分比
```
### 方案B: BYOK模型的显式配置
如果BYOK模型不提供capabilities信息需要用户手动设置
```python
class Valves(BaseModel):
# ... existing config ...
BYOK_CONTEXT_WINDOW: int = Field(
default=0, # 0表示自动检测或禁用compression
description="Manual context window size for BYOK models (tokens). 0=auto-detect or disabled"
)
BYOK_INFINITE_SESSION: bool = Field(
default=False,
description="Enable infinite sessions for BYOK models (requires BYOK_CONTEXT_WINDOW > 0)"
)
```
### 方案C: 从会话反馈中学习(最可靠)
```python
# infinite session压缩完成时获取实际的context window使用情况
# (需要SDK或CLI提供反馈)
```
---
## 建议实施路线
**优先级1必须**: 检查BYOK模式下是否能获取capabilities
```python
# 测试代码
if is_byok_model:
# 发送一个测试请求看是否能从响应中获取model capabilities
session = await client.create_session(config=session_config)
# session是否包含model info
# 能否访问session.model_capabilities
```
**优先级2重要**: 如果BYOK没有capabilities添加手动配置
```python
# 在BYOK配置中添加context_window字段
BYOK_CONTEXT_WINDOW: int = Field(default=0)
```
**优先级3长期**: 利用真实的上下文窗口来调整压缩策略
```python
# 而不是单纯的百分比使用实际的token数
```
---
## 关键问题列表
1. [ ] BYOK模型在create_session后能否获取capabilities信息
2. [ ] 如果能获取max_context_window_tokens的值是否准确
3. [ ] 如果不能获取,是否需要用户手动提供?
4. [ ] 当前的0.80/0.95阈值是否对所有模型都适用?
5. [ ] 不同的BYOK提供商(OpenAI vs Anthropic)的上下文窗口差异有多大?

View File

@@ -0,0 +1,142 @@
import asyncio
import json
import sys
from typing import Any, Callable
from copilot import CopilotClient
try:
from copilot import PermissionHandler
except ImportError:
PermissionHandler = None
def _to_dict(obj: Any) -> dict:
if obj is None:
return {}
to_dict = getattr(obj, "to_dict", None)
if callable(to_dict):
return to_dict()
if isinstance(obj, dict):
return obj
result = {}
for key in ("name", "display_name", "description"):
if hasattr(obj, key):
result[key] = getattr(obj, key)
return result
def _extract_agents(result: Any) -> list[dict]:
if result is None:
return []
if isinstance(result, dict):
raw_agents = result.get("agents")
else:
raw_agents = getattr(result, "agents", None)
if not raw_agents:
return []
normalized = []
for item in raw_agents:
data = _to_dict(item)
normalized.append(
{
"name": str(data.get("name", "") or "").strip(),
"display_name": str(data.get("display_name", "") or "").strip(),
"description": str(data.get("description", "") or "").strip(),
}
)
return normalized
def _extract_current_agent(result: Any) -> dict | None:
if result is None:
return None
if isinstance(result, dict):
agent = result.get("agent")
else:
agent = getattr(result, "agent", None)
if not agent:
return None
data = _to_dict(agent)
return {
"name": str(data.get("name", "") or "").strip(),
"display_name": str(data.get("display_name", "") or "").strip(),
"description": str(data.get("description", "") or "").strip(),
}
async def main() -> int:
client = CopilotClient()
started = False
session = None
try:
await client.start()
started = True
session_config: dict[str, Any] = {}
permission_handler: Callable | None = getattr(
PermissionHandler, "approve_all", None
)
if callable(permission_handler):
session_config["on_permission_request"] = permission_handler
session = await client.create_session(session_config)
list_result = await session.rpc.agent.list()
current_result = await session.rpc.agent.get_current()
agents = _extract_agents(list_result)
current = _extract_current_agent(current_result)
payload = {
"agents_count": len(agents),
"agents": agents,
"current_agent": current,
"summary": (
"No custom agents detected in current runtime."
if not agents
else "Custom agents detected."
),
}
print(json.dumps(payload, ensure_ascii=False, indent=2))
if not agents:
print("\n[INFO] 当前运行时没有已注入的 custom agents默认通常为空")
elif not current:
print("\n[INFO] 已检测到 custom agents但当前没有选中的 agent。")
else:
print(
"\n[INFO] 当前已选中 agent: "
f"{current.get('display_name') or current.get('name') or '(unknown)'}"
)
return 0
except Exception as exc:
print(f"[ERROR] Agent 检测失败: {exc}", file=sys.stderr)
return 1
finally:
if session is not None:
try:
await session.destroy()
except Exception:
pass
if started:
try:
await client.stop()
except Exception:
pass
if __name__ == "__main__":
raise SystemExit(asyncio.run(main()))

View File

@@ -0,0 +1,305 @@
# OpenWebUI Skills Manager 安全修复测试指南
## 快速开始
### 无需 OpenWebUI 依赖的独立测试
已创建完全独立的测试脚本,**不需要任何 OpenWebUI 依赖**,可以直接运行:
```bash
python3 plugins/debug/openwebui-skills-manager/test_security_fixes.py
```
### 测试输出示例
```
🔒 OpenWebUI Skills Manager 安全修复测试
版本: 0.2.2
============================================================
✓ 所有测试通过!
修复验证:
✓ SSRF 防护:阻止指向内部 IP 的请求
✓ TAR/ZIP 安全提取:防止路径遍历攻击
✓ 名称冲突检查:防止技能名称重复
✓ URL 验证:仅接受安全的 HTTP(S) URL
```
---
## 五个测试用例详解
### 1. SSRF 防护测试
**文件**: `test_security_fixes.py` - `test_ssrf_protection()`
测试 `_is_safe_url()` 方法能否正确识别并拒绝危险的 URL
<details>
<summary>被拒绝的 URL (10 种)</summary>
```
✗ http://localhost/skill
✗ http://127.0.0.1:8000/skill # 127.0.0.1 环回地址
✗ http://[::1]/skill # IPv6 环回
✗ http://0.0.0.0/skill # 全零 IP
✗ http://192.168.1.1/skill # RFC 1918 私有范围
✗ http://10.0.0.1/skill # RFC 1918 私有范围
✗ http://172.16.0.1/skill # RFC 1918 私有范围
✗ http://169.254.1.1/skill # Link-local
✗ file:///etc/passwd # file:// 协议
✗ gopher://example.com/skill # 非 http(s)
```
</details>
<details>
<summary>被接受的 URL (3 种)</summary>
```
✓ https://github.com/Fu-Jie/openwebui-extensions/raw/main/SKILL.md
✓ https://raw.githubusercontent.com/user/repo/main/skill.md
✓ https://example.com/public/skill.zip
```
</details>
**防护机制**:
- 检查 hostname 是否在 localhost 变体列表中
- 使用 `ipaddress` 库检测私有、回环、链接本地和保留 IP
- 仅允许 `http``https` 协议
---
### 2. TAR 提取安全性测试
**文件**: `test_security_fixes.py` - `test_tar_extraction_safety()`
测试 `_safe_extract_tar()` 方法能否防止**路径遍历攻击**
**被测试的攻击**:
```
TAR 文件包含: ../../etc/passwd
提取时被拦截,日志输出:
WARNING - Skipping unsafe TAR member: ../../etc/passwd
结果: /etc/passwd 文件 NOT 创建 ✓
```
**防护机制**:
```python
# 验证解析后的路径是否在提取目录内
member_path.resolve().relative_to(extract_dir.resolve())
# 如果抛出 ValueError说明有遍历尝试跳过该成员
```
---
### 3. ZIP 提取安全性测试
**文件**: `test_security_fixes.py` - `test_zip_extraction_safety()`
与 TAR 测试相同,但针对 ZIP 文件的路径遍历防护:
```
ZIP 文件包含: ../../etc/passwd
提取时被拦截
结果: /etc/passwd 文件 NOT 创建 ✓
```
---
### 4. 技能名称冲突检查测试
**文件**: `test_security_fixes.py` - `test_skill_name_collision()`
测试 `update_skill()` 方法中的名称碰撞检查:
```
场景 1: 尝试将技能2改名为 "MySkill" (已被技能1占用)
检查逻辑触发,检测到冲突
返回错误: Another skill already has the name "MySkill" ✓
场景 2: 尝试将技能2改名为 "UniqueSkill" (不存在)
检查通过,允许改名 ✓
```
---
### 5. URL 标准化测试
**文件**: `test_security_fixes.py` - `test_url_normalization()`
测试 URL 验证对各种无效格式的处理:
```
被拒绝的无效 URL:
✗ not-a-url # 不是有效 URL
✗ ftp://example.com # 非 http/https 协议
✗ "" # 空字符串
✗ " " # 纯空白
```
---
## 如何修改和扩展测试
### 添加自己的测试用例
编辑 `plugins/debug/openwebui-skills-manager/test_security_fixes.py`
```python
def test_my_custom_case():
"""我的自定义测试"""
print("\n" + "="*60)
print("测试 X: 我的自定义测试")
print("="*60)
tester = SecurityTester()
# 你的测试代码
assert condition, "错误消息"
print("\n✓ 自定义测试通过!")
# 在 main() 中添加
def main():
# ...
test_my_custom_case() # 新增
# ...
```
### 测试特定的 URL
直接在 `unsafe_urls``safe_urls` 列表中添加:
```python
unsafe_urls = [
# 现有项
"http://internal-server.local/api", # 新增: 本地局域网
]
safe_urls = [
# 现有项
"https://api.github.com/repos/Fu-Jie/openwebui-extensions", # 新增
]
```
---
## 与 OpenWebUI 集成测试
如果需要在完整的 OpenWebUI 环境中测试,可以:
### 1. 单元测试方式
创建 `tests/test_skills_manager.py`(需要 OpenWebUI 环境):
```python
import pytest
from plugins.tools.openwebui_skills_manager.openwebui_skills_manager import Tool
@pytest.fixture
def skills_tool():
return Tool()
def test_safe_url_in_tool(skills_tool):
"""在实际工具对象中测试"""
assert not skills_tool._is_safe_url("http://localhost/skill")
assert skills_tool._is_safe_url("https://github.com/user/repo")
```
运行方式:
```bash
pytest tests/test_skills_manager.py -v
```
### 2. 集成测试方式
在 OpenWebUI 中手动测试:
1. **安装插件**:
```
OpenWebUI → Admin → Tools → 添加 openwebui-skills-manager 工具
```
2. **测试 SSRF 防护**:
```
调用: install_skill(url="http://localhost:8000/skill.md")
预期: 返回错误 "Unsafe URL: points to internal or reserved destination"
```
3. **测试名称冲突**:
```
1. create_skill(name="MySkill", ...)
2. create_skill(name="AnotherSkill", ...)
3. update_skill(name="AnotherSkill", new_name="MySkill")
预期: 返回错误 "Another skill already has the name..."
```
4. **测试文件提取**:
```
上传包含 ../../etc/passwd 的恶意 TAR/ZIP
预期: 提取成功但恶意文件被跳过
```
---
## 故障排除
### 问题: `ModuleNotFoundError: No module named 'ipaddress'`
**解决**: `ipaddress` 是内置模块,无需安装。检查 Python 版本 >= 3.3
```bash
python3 --version # 应该 >= 3.3
```
### 问题: 测试卡住
**解决**: TAR/ZIP 提取涉及文件 I/O可能在某些系统上较慢。检查磁盘空间
```bash
df -h # 检查是否有足够空间
```
### 问题: 权限错误
**解决**: 确认脚本可执行:
```bash
chmod +x plugins/debug/openwebui-skills-manager/test_security_fixes.py
```
---
## 修复验证清单
- [x] SSRF 防护 - 阻止内部 IP 请求
- [x] TAR 提取安全 - 防止路径遍历
- [x] ZIP 提取安全 - 防止路径遍历
- [x] 名称冲突检查 - 防止重名技能
- [x] 注释更正 - 移除误导性文档
- [x] 版本更新 - 0.2.2
---
## 相关链接
- GitHub Issue: <https://github.com/Fu-Jie/openwebui-extensions/issues/58>
- 修改文件: `plugins/tools/openwebui-skills-manager/openwebui_skills_manager.py`
- 测试文件: `plugins/debug/openwebui-skills-manager/test_security_fixes.py`

View File

@@ -0,0 +1,560 @@
#!/usr/bin/env python3
"""
独立测试脚本:验证 OpenWebUI Skills Manager 的所有安全修复
不需要 OpenWebUI 环境,可以直接运行
测试内容:
1. SSRF 防护 (_is_safe_url)
2. 不安全 tar/zip 提取防护 (_safe_extract_zip, _safe_extract_tar)
3. 名称冲突检查 (update_skill)
4. URL 验证
"""
import asyncio
import json
import logging
import sys
import tempfile
import tarfile
import zipfile
from pathlib import Path
from typing import Optional, Dict, Any, List, Tuple
# 配置日志
logging.basicConfig(
level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)
# ==================== 模拟 OpenWebUI Skills 类 ====================
class MockSkill:
def __init__(self, id: str, name: str, description: str = "", content: str = ""):
self.id = id
self.name = name
self.description = description
self.content = content
self.is_active = True
self.updated_at = "2024-03-08T00:00:00Z"
class MockSkills:
"""Mock Skills 模型,用于测试"""
_skills: Dict[str, List[MockSkill]] = {}
@classmethod
def reset(cls):
cls._skills = {}
@classmethod
def get_skills_by_user_id(cls, user_id: str):
return cls._skills.get(user_id, [])
@classmethod
def insert_new_skill(cls, user_id: str, form_data):
if user_id not in cls._skills:
cls._skills[user_id] = []
skill = MockSkill(
form_data.id, form_data.name, form_data.description, form_data.content
)
cls._skills[user_id].append(skill)
return skill
@classmethod
def update_skill_by_id(cls, skill_id: str, updates: Dict[str, Any]):
for user_skills in cls._skills.values():
for skill in user_skills:
if skill.id == skill_id:
for key, value in updates.items():
setattr(skill, key, value)
return skill
return None
@classmethod
def delete_skill_by_id(cls, skill_id: str):
for user_id, user_skills in cls._skills.items():
for idx, skill in enumerate(user_skills):
if skill.id == skill_id:
user_skills.pop(idx)
return True
return False
# ==================== 提取安全测试的核心方法 ====================
import ipaddress
import urllib.parse
class SecurityTester:
"""提取出的安全测试核心类"""
def __init__(self):
# 模拟 Valves 配置
self.valves = type(
"Valves",
(),
{
"ENABLE_DOMAIN_WHITELIST": True,
"TRUSTED_DOMAINS": "github.com,raw.githubusercontent.com,huggingface.co",
},
)()
def _is_safe_url(self, url: str) -> tuple:
"""
验证 URL 是否指向内部/敏感目标。
防止服务端请求伪造 (SSRF) 攻击。
返回 (True, None) 如果 URL 是安全的,否则返回 (False, error_message)。
"""
try:
parsed = urllib.parse.urlparse(url)
hostname = parsed.hostname or ""
if not hostname:
return False, "URL is malformed: missing hostname"
# 拒绝 localhost 变体
if hostname.lower() in (
"localhost",
"127.0.0.1",
"::1",
"[::1]",
"0.0.0.0",
"[::ffff:127.0.0.1]",
"localhost.localdomain",
):
return False, "URL points to local host"
# 拒绝内部 IP 范围 (RFC 1918, link-local 等)
try:
ip = ipaddress.ip_address(hostname.lstrip("[").rstrip("]"))
# 拒绝私有、回环、链接本地和保留 IP
if (
ip.is_private
or ip.is_loopback
or ip.is_link_local
or ip.is_reserved
):
return False, f"URL points to internal IP: {ip}"
except ValueError:
# 不是 IP 地址,检查 hostname 模式
pass
# 拒绝 file:// 和其他非 http(s) 方案
if parsed.scheme not in ("http", "https"):
return False, f"URL scheme not allowed: {parsed.scheme}"
# 域名白名单检查 (安全层 2)
if self.valves.ENABLE_DOMAIN_WHITELIST:
trusted_domains = [
d.strip().lower()
for d in (self.valves.TRUSTED_DOMAINS or "").split(",")
if d.strip()
]
if not trusted_domains:
# 没有配置授信域名,仅进行安全检查
return True, None
hostname_lower = hostname.lower()
# 检查 hostname 是否匹配任何授信域名(精确或子域名)
is_trusted = False
for trusted_domain in trusted_domains:
# 精确匹配
if hostname_lower == trusted_domain:
is_trusted = True
break
# 子域名匹配 (*.example.com 匹配 api.example.com)
if hostname_lower.endswith("." + trusted_domain):
is_trusted = True
break
if not is_trusted:
error_msg = f"URL domain '{hostname}' is not in whitelist. Trusted domains: {', '.join(trusted_domains)}"
return False, error_msg
return True, None
except Exception as e:
return False, f"Error validating URL: {e}"
def _safe_extract_zip(self, zip_path: Path, extract_dir: Path) -> None:
"""
安全地提取 ZIP 文件,验证成员路径以防止路径遍历。
"""
with zipfile.ZipFile(zip_path, "r") as zf:
for member in zf.namelist():
# 检查路径遍历尝试
member_path = Path(extract_dir) / member
try:
# 确保解析的路径在 extract_dir 内
member_path.resolve().relative_to(extract_dir.resolve())
except ValueError:
# 路径在 extract_dir 外(遍历尝试)
logger.warning(f"Skipping unsafe ZIP member: {member}")
continue
# 提取成员
zf.extract(member, extract_dir)
def _safe_extract_tar(self, tar_path: Path, extract_dir: Path) -> None:
"""
安全地提取 TAR 文件,验证成员路径以防止路径遍历。
"""
with tarfile.open(tar_path, "r:*") as tf:
for member in tf.getmembers():
# 检查路径遍历尝试
member_path = Path(extract_dir) / member.name
try:
# 确保解析的路径在 extract_dir 内
member_path.resolve().relative_to(extract_dir.resolve())
except ValueError:
# 路径在 extract_dir 外(遍历尝试)
logger.warning(f"Skipping unsafe TAR member: {member.name}")
continue
# 提取成员
tf.extract(member, extract_dir)
# ==================== 测试用例 ====================
def test_ssrf_protection():
"""测试 SSRF 防护"""
print("\n" + "=" * 60)
print("测试 1: SSRF 防护 (_is_safe_url)")
print("=" * 60)
tester = SecurityTester()
# 不安全的 URLs (应该被拒绝)
unsafe_urls = [
"http://localhost/skill",
"http://127.0.0.1:8000/skill",
"http://[::1]/skill",
"http://0.0.0.0/skill",
"http://192.168.1.1/skill", # 私有 IP (RFC 1918)
"http://10.0.0.1/skill",
"http://172.16.0.1/skill",
"http://169.254.1.1/skill", # link-local
"file:///etc/passwd", # file:// scheme
"gopher://example.com/skill", # 非 http(s)
]
print("\n❌ 不安全的 URLs (应该被拒绝):")
for url in unsafe_urls:
is_safe, error_msg = tester._is_safe_url(url)
status = "✗ 被拒绝 (正确)" if not is_safe else "✗ 被接受 (错误)"
error_info = f" - {error_msg}" if error_msg else ""
print(f" {url:<50} {status}{error_info}")
assert not is_safe, f"URL 不应该被接受: {url}"
# 安全的 URLs (应该被接受)
safe_urls = [
"https://github.com/Fu-Jie/openwebui-extensions/raw/main/SKILL.md",
"https://raw.githubusercontent.com/user/repo/main/skill.md",
"https://huggingface.co/spaces/user/skill",
]
print("\n✅ 安全且在白名单中的 URLs (应该被接受):")
for url in safe_urls:
is_safe, error_msg = tester._is_safe_url(url)
status = "✓ 被接受 (正确)" if is_safe else "✓ 被拒绝 (错误)"
error_info = f" - {error_msg}" if error_msg else ""
print(f" {url:<60} {status}{error_info}")
assert is_safe, f"URL 不应该被拒绝: {url} - {error_msg}"
print("\n✓ SSRF 防护测试通过!")
def test_tar_extraction_safety():
"""测试 TAR 提取路径遍历防护"""
print("\n" + "=" * 60)
print("测试 2: TAR 提取安全性 (_safe_extract_tar)")
print("=" * 60)
tester = SecurityTester()
with tempfile.TemporaryDirectory() as tmpdir:
tmpdir_path = Path(tmpdir)
# 创建一个包含路径遍历尝试的 tar 文件
tar_path = tmpdir_path / "malicious.tar"
extract_dir = tmpdir_path / "extracted"
extract_dir.mkdir(parents=True, exist_ok=True)
print("\n创建测试 TAR 文件...")
with tarfile.open(tar_path, "w") as tf:
# 合法的成员
import io
info = tarfile.TarInfo(name="safe_file.txt")
info.size = 11
tf.addfile(tarinfo=info, fileobj=io.BytesIO(b"safe content"))
# 路径遍历尝试
info = tarfile.TarInfo(name="../../etc/passwd")
info.size = 10
tf.addfile(tarinfo=info, fileobj=io.BytesIO(b"evil data!"))
print(f" TAR 文件已创建: {tar_path}")
# 提取文件
print("\n提取 TAR 文件...")
try:
tester._safe_extract_tar(tar_path, extract_dir)
# 检查结果
safe_file = extract_dir / "safe_file.txt"
evil_file = extract_dir / "etc" / "passwd"
evil_file_alt = Path("/etc/passwd")
print(f" 检查合法文件: {safe_file.exists()} (应该为 True)")
assert safe_file.exists(), "合法文件应该被提取"
print(f" 检查恶意文件不存在: {not evil_file.exists()} (应该为 True)")
assert not evil_file.exists(), "恶意文件不应该被提取"
print("\n✓ TAR 提取安全性测试通过!")
except Exception as e:
print(f"✗ 提取失败: {e}")
raise
def test_zip_extraction_safety():
"""测试 ZIP 提取路径遍历防护"""
print("\n" + "=" * 60)
print("测试 3: ZIP 提取安全性 (_safe_extract_zip)")
print("=" * 60)
tester = SecurityTester()
with tempfile.TemporaryDirectory() as tmpdir:
tmpdir_path = Path(tmpdir)
# 创建一个包含路径遍历尝试的 zip 文件
zip_path = tmpdir_path / "malicious.zip"
extract_dir = tmpdir_path / "extracted"
extract_dir.mkdir(parents=True, exist_ok=True)
print("\n创建测试 ZIP 文件...")
with zipfile.ZipFile(zip_path, "w") as zf:
# 合法的成员
zf.writestr("safe_file.txt", "safe content")
# 路径遍历尝试
zf.writestr("../../etc/passwd", "evil data!")
print(f" ZIP 文件已创建: {zip_path}")
# 提取文件
print("\n提取 ZIP 文件...")
try:
tester._safe_extract_zip(zip_path, extract_dir)
# 检查结果
safe_file = extract_dir / "safe_file.txt"
evil_file = extract_dir / "etc" / "passwd"
print(f" 检查合法文件: {safe_file.exists()} (应该为 True)")
assert safe_file.exists(), "合法文件应该被提取"
print(f" 检查恶意文件不存在: {not evil_file.exists()} (应该为 True)")
assert not evil_file.exists(), "恶意文件不应该被提取"
print("\n✓ ZIP 提取安全性测试通过!")
except Exception as e:
print(f"✗ 提取失败: {e}")
raise
def test_skill_name_collision():
"""测试技能名称冲突检查"""
print("\n" + "=" * 60)
print("测试 4: 技能名称冲突检查")
print("=" * 60)
# 模拟技能管理
user_id = "test_user_1"
MockSkills.reset()
# 创建第一个技能
print("\n创建技能 1: 'MySkill'...")
skill1 = MockSkill("skill_1", "MySkill", "First skill", "content1")
MockSkills._skills[user_id] = [skill1]
print(f" ✓ 技能已创建: {skill1.name}")
# 创建第二个技能
print("\n创建技能 2: 'AnotherSkill'...")
skill2 = MockSkill("skill_2", "AnotherSkill", "Second skill", "content2")
MockSkills._skills[user_id].append(skill2)
print(f" ✓ 技能已创建: {skill2.name}")
# 测试名称冲突检查逻辑
print("\n测试名称冲突检查...")
# 模拟尝试将 skill2 改名为 skill1 的名称
new_name = "MySkill" # 已被 skill1 占用
print(f"\n尝试将技能 2 改名为 '{new_name}'...")
print(f" 检查是否与其他技能冲突...")
# 这是 update_skill 中的冲突检查逻辑
collision_found = False
for other_skill in MockSkills._skills[user_id]:
# 跳过要更新的技能本身
if other_skill.id == "skill_2":
continue
# 检查是否存在同名技能
if other_skill.name.lower() == new_name.lower():
collision_found = True
print(f" ✓ 冲突检测成功!发现重复名称: {other_skill.name}")
break
assert collision_found, "应该检测到名称冲突"
# 测试允许的改名(改为不同的名称)
print(f"\n尝试将技能 2 改名为 'UniqueSkill'...")
new_name = "UniqueSkill"
collision_found = False
for other_skill in MockSkills._skills[user_id]:
if other_skill.id == "skill_2":
continue
if other_skill.name.lower() == new_name.lower():
collision_found = True
break
assert not collision_found, "不应该存在冲突"
print(f" ✓ 允许改名,没有冲突")
print("\n✓ 技能名称冲突检查测试通过!")
def test_url_normalization():
"""测试 URL 标准化"""
print("\n" + "=" * 60)
print("测试 5: URL 标准化")
print("=" * 60)
tester = SecurityTester()
# 测试无效的 URL
print("\n测试无效的 URL:")
invalid_urls = [
"not-a-url",
"ftp://example.com/file",
"",
" ",
]
for url in invalid_urls:
is_safe, error_msg = tester._is_safe_url(url)
print(f" '{url}' -> 被拒绝: {not is_safe}")
assert not is_safe, f"无效 URL 应该被拒绝: {url}"
print("\n✓ URL 标准化测试通过!")
def test_domain_whitelist():
"""测试域名白名单功能"""
print("\n" + "=" * 60)
print("测试 6: 域名白名单 (ENABLE_DOMAIN_WHITELIST)")
print("=" * 60)
# 创建启用白名单的测试器
tester = SecurityTester()
tester.valves.ENABLE_DOMAIN_WHITELIST = True
tester.valves.TRUSTED_DOMAINS = (
"github.com,raw.githubusercontent.com,huggingface.co"
)
print("\n配置信息:")
print(f" 白名单启用: {tester.valves.ENABLE_DOMAIN_WHITELIST}")
print(f" 授信域名: {tester.valves.TRUSTED_DOMAINS}")
# 白名单中的 URLs (应该被接受)
whitelisted_urls = [
"https://github.com/user/repo/raw/main/skill.md",
"https://raw.githubusercontent.com/user/repo/main/skill.md",
"https://api.github.com/repos/user/repo/contents",
"https://huggingface.co/spaces/user/skill",
]
print("\n✅ 白名单中的 URLs (应该被接受):")
for url in whitelisted_urls:
is_safe, error_msg = tester._is_safe_url(url)
status = "✓ 被接受 (正确)" if is_safe else "✗ 被拒绝 (错误)"
print(f" {url:<65} {status}")
assert is_safe, f"白名单中的 URL 应该被接受: {url} - {error_msg}"
# 不在白名单中的 URLs (应该被拒绝)
non_whitelisted_urls = [
"https://example.com/skill.md",
"https://evil.com/skill.zip",
"https://api.example.com/skill",
]
print("\n❌ 非白名单 URLs (应该被拒绝):")
for url in non_whitelisted_urls:
is_safe, error_msg = tester._is_safe_url(url)
status = "✗ 被拒绝 (正确)" if not is_safe else "✓ 被接受 (错误)"
print(f" {url:<65} {status}")
assert not is_safe, f"非白名单 URL 应该被拒绝: {url}"
# 测试禁用白名单
print("\n禁用白名单进行测试...")
tester.valves.ENABLE_DOMAIN_WHITELIST = False
is_safe, error_msg = tester._is_safe_url("https://example.com/skill.md")
print(f" example.com without whitelist: {is_safe}")
assert is_safe, "禁用白名单时example.com 应该被接受"
print("\n✓ 域名白名单测试通过!")
# ==================== 主函数 ====================
def main():
print("\n" + "🔒 OpenWebUI Skills Manager 安全修复测试".center(60, "="))
print("版本: 0.2.2")
print("=" * 60)
try:
# 运行所有测试
test_ssrf_protection()
test_tar_extraction_safety()
test_zip_extraction_safety()
test_skill_name_collision()
test_url_normalization()
test_domain_whitelist()
# 测试总结
print("\n" + "=" * 60)
print("🎉 所有测试通过!".center(60))
print("=" * 60)
print("\n修复验证:")
print(" ✓ SSRF 防护:阻止指向内部 IP 的请求")
print(" ✓ TAR/ZIP 安全提取:防止路径遍历攻击")
print(" ✓ 名称冲突检查:防止技能名称重复")
print(" ✓ URL 验证:仅接受安全的 HTTP(S) URL")
print(" ✓ 域名白名单:只允许授信域名下载技能")
print("\n所有安全功能都已成功实现!")
print("=" * 60 + "\n")
return 0
except AssertionError as e:
print(f"\n❌ 测试失败: {e}\n")
return 1
except Exception as e:
print(f"\n❌ 测试错误: {e}\n")
import traceback
traceback.print_exc()
return 1
if __name__ == "__main__":
sys.exit(main())

View File

@@ -0,0 +1,354 @@
# ✨ 异步上下文压缩本地部署工具 — 完整文件清单
## 📦 新增文件总览
为 async_context_compression Filter 插件增加的本地部署功能包括:
```
openwebui-extensions/
├── scripts/
│ ├── ✨ deploy_async_context_compression.py (新增) 专用部署脚本 [70 行]
│ ├── ✨ deploy_filter.py (新增) 通用 Filter 部署工具 [300 行]
│ ├── ✨ DEPLOYMENT_GUIDE.md (新增) 完整部署指南 [详细]
│ ├── ✨ DEPLOYMENT_SUMMARY.md (新增) 技术架构总结 [详细]
│ ├── ✨ QUICK_START.md (新增) 快速参考卡片 [速查]
│ ├── ✨ README.md (新增) 脚本使用说明 [本文]
│ └── deploy_pipe.py (已有) Pipe 部署工具
└── tests/
└── scripts/
└── ✨ test_deploy_filter.py (新增) 单元测试 [10个测试 ✅]
```
## 🎯 快速使用
### 最简单的方式 — 一行命令
```bash
cd scripts && python deploy_async_context_compression.py
```
**✅ 结果**:
- async_context_compression Filter 被部署到本地 OpenWebUI
- 无需重启 OpenWebUI立即生效
- 显示部署状态和后续步骤
### 第一次使用建议
```bash
# 1. 进入 scripts 目录
cd scripts
# 2. 查看所有可用的部署脚本
ls -la deploy_*.py
# 3. 阅读快速开始指南
cat QUICK_START.md
# 4. 部署 async_context_compression
python deploy_async_context_compression.py
```
## 📚 文件详细说明
### 1. `deploy_async_context_compression.py` ⭐ 推荐
**最快速的部署方式!**
```bash
python deploy_async_context_compression.py
```
**特点**:
- 专为 async_context_compression 优化
- 一条命令完成部署
- 清晰的成功/失败提示
- 显示后续配置步骤
**代码**: 约 70 行,简洁清晰
---
### 2. `deploy_filter.py` — 通用工具
支持部署 **所有 Filter 插件**
```bash
# 默认部署 async_context_compression
python deploy_filter.py
# 部署其他 Filter
python deploy_filter.py folder-memory
python deploy_filter.py context_enhancement_filter
# 列出所有可用 Filter
python deploy_filter.py --list
```
**特点**:
- 通用的 Filter 部署框架
- 自动元数据提取
- 支持多个插件
- 智能错误处理
**代码**: 约 300 行,完整功能
---
### 3. `QUICK_START.md` — 快速参考
一页纸的速查表,包含:
- ⚡ 30秒快速开始
- 📋 常见命令表格
- ❌ 故障排除速查
**适合**: 第二次及以后使用
---
### 4. `DEPLOYMENT_GUIDE.md` — 完整指南
详细的部署指南,包含:
- 前置条件检查
- 分步工作流
- API 密钥获取方法
- 详细的故障排除
- CI/CD 集成示例
**适合**: 首次部署或需要深入了解
---
### 5. `DEPLOYMENT_SUMMARY.md` — 技术总结
技术架构和实现细节:
- 工作原理流程图
- 元数据提取机制
- API 集成说明
- 安全最佳实践
**适合**: 开发者和想了解实现的人
---
### 6. `test_deploy_filter.py` — 单元测试
完整的测试覆盖:
```bash
pytest tests/scripts/test_deploy_filter.py -v
```
**测试内容**: 10 个单元测试 ✅
- Filter 发现
- 元数据提取
- 负载构建
- 版本处理
---
## 🚀 三个使用场景
### 场景 1: 快速部署(最常用)
```bash
cd scripts
python deploy_async_context_compression.py
# 完成!✅
```
**耗时**: 5 秒
**适合**: 日常开发迭代
---
### 场景 2: 部署其他 Filter
```bash
cd scripts
python deploy_filter.py --list # 查看所有
python deploy_filter.py folder-memory # 部署指定的
```
**耗时**: 5 秒 × N
**适合**: 管理多个 Filter
---
### 场景 3: 完整设置(首次)
```bash
cd scripts
# 1. 创建 API 密钥配置
echo "api_key=sk-your-key" > .env
# 2. 验证配置
cat .env
# 3. 部署
python deploy_async_context_compression.py
# 4. 查看结果
curl http://localhost:3003/api/v1/functions
```
**耗时**: 1 分钟
**适合**: 第一次设置
---
## 📋 文件访问指南
| 我想... | 文件 | 命令 |
|---------|------|------|
| 部署 async_context_compression | deploy_async_context_compression.py | `python deploy_async_context_compression.py` |
| 看快速参考 | QUICK_START.md | `cat QUICK_START.md` |
| 完整指南 | DEPLOYMENT_GUIDE.md | `cat DEPLOYMENT_GUIDE.md` |
| 技术细节 | DEPLOYMENT_SUMMARY.md | `cat DEPLOYMENT_SUMMARY.md` |
| 运行测试 | test_deploy_filter.py | `pytest tests/scripts/test_deploy_filter.py -v` |
| 部署其他 Filter | deploy_filter.py | `python deploy_filter.py --list` |
## ✅ 验证清单
确保一切就绪:
```bash
# 1. 检查所有部署脚本都已创建
ls -la scripts/deploy*.py
# 应该看到: deploy_pipe.py, deploy_filter.py, deploy_async_context_compression.py
# 2. 检查所有文档都已创建
ls -la scripts/*.md
# 应该看到: DEPLOYMENT_GUIDE.md, DEPLOYMENT_SUMMARY.md, QUICK_START.md, README.md
# 3. 检查测试存在
ls -la tests/scripts/test_deploy_filter.py
# 4. 运行一次测试验证
python -m pytest tests/scripts/test_deploy_filter.py -v
# 应该看到: 10 passed ✅
# 5. 尝试部署
cd scripts && python deploy_async_context_compression.py
```
## 🎓 学习路径
### 初学者路径
```
1. 阅读本文件 (5 分钟)
2. 阅读 QUICK_START.md (5 分钟)
3. 运行部署脚本 (5 分钟)
4. 在 OpenWebUI 中测试 (5 分钟)
```
### 开发者路径
```
1. 阅读本文件
2. 阅读 DEPLOYMENT_GUIDE.md
3. 阅读 DEPLOYMENT_SUMMARY.md
4. 查看源代码: deploy_filter.py
5. 运行测试: pytest tests/scripts/test_deploy_filter.py -v
```
## 🔧 常见问题
### Q: 如何更新已部署的插件?
```bash
# 修改代码后
vim ../plugins/filters/async-context-compression/async_context_compression.py
# 重新部署(自动覆盖)
python deploy_async_context_compression.py
```
### Q: 支持哪些 Filter
```bash
python deploy_filter.py --list
```
### Q: 如何获取 API 密钥?
1. 打开 OpenWebUI
2. 点击用户菜单 → Settings
3. 找到 "API Keys" 部分
4. 复制密钥到 `.env` 文件
### Q: 脚本失败了怎么办?
1. 查看错误信息
2. 参考 `QUICK_START.md` 的故障排除部分
3. 或查看 `DEPLOYMENT_GUIDE.md` 的详细说明
### Q: 安全吗?
✅ 完全安全
- API 密钥存储在本地 `.env` 文件
- `.env` 已添加到 `.gitignore`
- 绝不会被提交到 Git
- 密钥可随时轮换
### Q: 可以在生产环境使用吗?
✅ 可以
- 生产环境建议通过 CI/CD 秘密管理
- 参考 `DEPLOYMENT_GUIDE.md` 中的 GitHub Actions 示例
## 🚦 快速状态检查
```bash
# 检查所有部署工具是否就绪
cd scripts
# 查看脚本列表
ls -la deploy*.py
# 查看文档列表
ls -la *.md | grep -i deploy
# 验证测试通过
python -m pytest tests/scripts/test_deploy_filter.py -q
# 执行部署
python deploy_async_context_compression.py
```
## 📞 下一步
1. **立即尝试**: `cd scripts && python deploy_async_context_compression.py`
2. **查看结果**: 打开 OpenWebUI → Settings → Filters → 找 "Async Context Compression"
3. **启用使用**: 在对话中启用这个 Filter体验上下文压缩功能
4. **继续开发**: 修改代码后重复部署过程
## 📝 更多资源
- 🚀 快速开始: [QUICK_START.md](QUICK_START.md)
- 📖 完整指南: [DEPLOYMENT_GUIDE.md](DEPLOYMENT_GUIDE.md)
- 🏗️ 技术架构: [DEPLOYMENT_SUMMARY.md](DEPLOYMENT_SUMMARY.md)
- 🧪 测试套件: [test_deploy_filter.py](../tests/scripts/test_deploy_filter.py)
---
## 📊 文件统计
```
新增 Python 脚本: 2 个 (deploy_filter.py, deploy_async_context_compression.py)
新增文档文件: 4 个 (DEPLOYMENT_*.md, QUICK_START.md)
新增测试文件: 1 个 (test_deploy_filter.py)
新增总代码行数: ~600 行
测试覆盖率: 10/10 单元测试通过 ✅
```
---
**创建日期**: 2026-03-09
**最好用于**: 本地开发和快速迭代
**维护者**: Fu-Jie
**项目**: [openwebui-extensions](https://github.com/Fu-Jie/openwebui-extensions)

View File

@@ -0,0 +1,189 @@
# Issue #56: Critical tool-calling corruption and multiple reliability issues
## Overview
This document consolidates all reported issues in the async-context-compression filter as described in [GitHub Issue #56](https://github.com/Fu-Jie/openwebui-extensions/issues/56).
---
## Issue List
### 1. 🔴 CRITICAL: Native tool-calling history can be corrupted
**Severity**: Critical
**Impact**: Conversation integrity
#### Description
The compression logic removes individual messages without preserving native tool-calling structures as atomic units. This can break the relationship between assistant `tool_calls` and their corresponding `tool` result messages.
#### Symptom
```
No tool call found for function call output with call_id ...
```
#### Root Cause
- Assistant messages containing `tool_calls` can be removed while their matching `tool` result messages remain
- This creates orphaned tool outputs that reference non-existent `tool_call_id`s
- The model/provider rejects the request because the `call_id` no longer matches any tool call in history
#### Expected Behavior
Compression must treat tool-calling blocks atomically:
- `assistant(tool_calls)` message
- Corresponding `tool` result message(s)
- Optional assistant follow-up that consumes tool results
Should never be split or partially removed.
---
### 2. 🟠 HIGH: Compression progress mixes original-history and compressed-view semantics
**Severity**: High
**Impact**: Summary advancement consistency
#### Description
The plugin stores `compressed_message_count` as progress over the original conversation history, but later recalculates it from the already-compressed conversation view. This mixes two different coordinate systems for the same field.
#### Problem
- Original-history progress (before compression)
- Compressed-view progress (after compression)
These two meanings are inconsistent, causing:
- Summary advancement to become inconsistent
- Summary progress to stall after summaries already exist
- Later updates to be measured in a different coordinate system than stored values
#### Expected Behavior
Progress tracking must use a single, consistent coordinate system throughout the lifetime of the conversation.
---
### 3. 🟡 MEDIUM: Async summary generation has no per-chat lock
**Severity**: Medium
**Impact**: Token usage, race conditions
#### Description
Each response can launch a new background summary task for the same chat, even if one is already in progress.
#### Problems
- Duplicate summary work
- Increased token usage
- Race conditions in saved summary state
- Potential data consistency issues
#### Expected Behavior
Use per-chat locking to ensure only one summary task runs per chat at a time.
---
### 4. 🟡 MEDIUM: Native tool-output trimming is too aggressive
**Severity**: Medium
**Impact**: Content accuracy in technical conversations
#### Description
The tool-output trimming heuristics can rewrite or trim normal assistant messages if they contain patterns such as:
- Code fences (triple backticks)
- `Arguments:` text
- `<tool_code>` tags
#### Problem
This is risky in technical conversations and may alter valid assistant content unintentionally.
#### Expected Behavior
Trimming logic should be more conservative and avoid modifying assistant messages that are not actually tool-output summaries.
---
### 5. 🟡 MEDIUM: `max_context_tokens = 0` has inconsistent semantics
**Severity**: Medium
**Impact**: Determinism, configuration clarity
#### Description
The setting `max_context_tokens = 0` behaves inconsistently across different code paths:
- In some paths: behaves like "no threshold" (special mode, no compression)
- In other paths: still triggers reduction/truncation logic
#### Problem
Non-deterministic behavior makes the setting unpredictable and confusing for users.
#### Expected Behavior
- Define clear semantics for `max_context_tokens = 0`
- Apply consistently across all code paths
- Document the intended behavior
---
### 6. 🔵 LOW: Corrupted Korean i18n string
**Severity**: Low
**Impact**: User experience for Korean speakers
#### Description
One translation string contains broken mixed-language text.
#### Expected Behavior
Clean up the Korean translation string to be properly formatted and grammatically correct.
---
## Related / Broader Context
**Note from issue reporter**: The critical bug is not limited to tool-calling fields alone. Because compression deletes or replaces whole message objects, it can also drop other per-message fields such as:
- Message-level `id`
- `metadata`
- `name`
- Similar per-message attributes
So the issue is broader than native tool-calling: any integration relying on per-message metadata may also be affected when messages are trimmed or replaced.
---
## Reproduction Steps
1. Start a chat with a model using native tool calling
2. Enable the async-context-compression filter
3. Send a conversation long enough to trigger compression / summary generation
4. Let the model perform multiple tool calls across several turns
5. Continue the same chat after the filter has already compressed part of the history
**Expected**: Chat continues normally
**Actual**: Chat can become desynchronized and fail with errors like `No tool call found for function call output with call_id ...`
**Control Test**:
- With filter disabled: failure does not occur
- With filter enabled: failure reproduces reliably
---
## Suggested Fix Direction
### High Priority (Blocks Issue #56)
1. **Preserve tool-calling atomicity**: Compress history in a way that never separates `assistant(tool_calls)` from its corresponding `tool` messages
2. **Unify progress tracking**: Use a single, consistent coordinate system for `compressed_message_count` throughout
3. **Add per-chat locking**: Ensure only one background summary task runs per chat at a time
### Medium Priority
4. **Conservative trimming**: Refine tool-output trimming heuristics to avoid altering valid assistant content
5. **Define `max_context_tokens = 0` semantics**: Make behavior consistent and predictable
6. **Fix i18n**: Clean up the corrupted Korean translation string
---
## Environment
- **Plugin**: async-context-compression
- **OpenWebUI Version**: 0.8.9
- **OS**: Ubuntu 24.04 LTS ARM64
- **Reported by**: @dhaern
- **Issue Date**: [Recently opened]
---
## References
- [GitHub Issue #56](https://github.com/Fu-Jie/openwebui-extensions/issues/56)
- Plugin: `plugins/filters/async-context-compression/async_context_compression.py`

View File

@@ -0,0 +1,189 @@
# Issue #56: 异步上下文压缩中的关键工具调用破坏和多个可靠性问题
## 概述
本文档汇总了 [GitHub Issue #56](https://github.com/Fu-Jie/openwebui-extensions/issues/56) 中所有关于异步上下文压缩过滤器的已报告问题。
---
## 问题列表
### 1. 🔴 关键:原生工具调用历史可能被破坏
**严重级别**: 关键
**影响范围**: 对话完整性
#### 描述
压缩逻辑逐条删除消息,而不是把原生工具调用结构作为原子整体保留。这可能会破坏 assistant `tool_calls` 与其对应 `tool` 结果消息的关系。
#### 症状
```
No tool call found for function call output with call_id ...
```
#### 根本原因
- 包含 `tool_calls` 的 assistant 消息可能被删除,但其对应的 `tool` 结果消息仍保留
- 这会产生孤立的工具输出,引用不存在的 `tool_call_id`
- 模型/API 提供商会拒绝该请求,因为 `call_id` 不再匹配历史中的任何工具调用
#### 期望行为
压缩必须把工具调用块当作原子整体对待:
- `assistant(tool_calls)` 消息
- 对应的 `tool` 结果消息
- 可选的 assistant 跟进消息(消费工具结果)
这些消息的任何部分都不应被分割或部分删除。
---
### 2. 🟠 高优先级:压缩进度混淆了原始历史和压缩视图语义
**严重级别**: 高
**影响范围**: 摘要进度一致性
#### 描述
插件将 `compressed_message_count` 存储为原始对话历史的进度,但稍后从已压缩的对话视图重新计算。这混淆了同一字段的两个不同坐标系。
#### 问题
- 原始历史进度(压缩前)
- 压缩视图进度(压缩后)
这两个含义不一致,造成:
- 摘要进度变得不一致
- 摘要已存在后进度可能停滞
- 后续更新用不同于存储值的坐标系测量
#### 期望行为
进度跟踪必须在对话整个生命周期中使用单一、一致的坐标系。
---
### 3. 🟡 中等优先级:异步摘要生成没有每聊天锁
**严重级别**: 中等
**影响范围**: 令牌使用、竞态条件
#### 描述
每个响应都可能为同一聊天启动新的后台摘要任务,即使已有任务在进行中。
#### 问题
- 摘要工作重复
- 令牌使用增加
- 已保存摘要状态出现竞态条件
- 数据一致性问题
#### 期望行为
使用每聊天锁机制确保每次只有一个摘要任务在该聊天中运行。
---
### 4. 🟡 中等优先级:原生工具输出裁剪太激进
**严重级别**: 中等
**影响范围**: 技术对话的内容准确性
#### 描述
工具输出裁剪启发式方法会重写或裁剪普通 assistant 消息,如果包含诸如以下模式:
- 代码围栏(三个反引号)
- `Arguments:` 文本
- `<tool_code>` 标签
#### 问题
这在技术对话中存在风险,可能无意中更改有效的 assistant 内容。
#### 期望行为
裁剪逻辑应更保守,避免修改非工具输出摘要的 assistant 消息。
---
### 5. 🟡 中等优先级:`max_context_tokens = 0` 语义不一致
**严重级别**: 中等
**影响范围**: 确定性、配置清晰度
#### 描述
设置 `max_context_tokens = 0` 在不同代码路径中行为不一致:
- 在某些路径中:像"无阈值"一样(特殊模式,无压缩)
- 在其他路径中:仍然触发缩减/截断逻辑
#### 问题
非确定性行为使设置变得不可预测和令人困惑。
#### 期望行为
-`max_context_tokens = 0` 定义清晰语义
- 在所有代码路径中一致应用
- 清楚地记录预期行为
---
### 6. 🔵 低优先级:破损的韩文 i18n 字符串
**严重级别**: 低
**影响范围**: 韩文使用者的用户体验
#### 描述
一个翻译字符串包含破损的混合语言文本。
#### 期望行为
清理韩文翻译字符串,使其格式正确和语法正确。
---
## 相关/更广泛的上下文
**问题报告者附注**:关键错误不仅限于工具调用字段。由于压缩删除或替换整个消息对象,它还可能丢弃其他每消息字段,例如:
- 消息级 `id`
- `metadata`
- `name`
- 其他每消息属性
因此问题范围广于原生工具调用:任何依赖每消息元数据的集成在消息被裁剪或替换时也可能受影响。
---
## 复现步骤
1. 使用原生工具调用启动与模型的聊天
2. 启用异步上下文压缩过滤器
3. 发送足够长的对话以触发压缩/摘要生成
4. 让模型在几个回合中执行多个工具调用
5. 在过滤器已压缩部分历史后继续同一聊天
**期望**: 聊天继续正常运行
**实际**: 聊天可能变得不同步并失败,出现错误如 `No tool call found for function call output with call_id ...`
**对照测试**:
- 禁用过滤器:不出现失败
- 启用过滤器:可靠地复现失败
---
## 建议的修复方向
### 高优先级(阻止 Issue #56
1. **保护工具调用原子性**:以不分割 `assistant(tool_calls)` 与其对应 `tool` 消息的方式压缩历史
2. **统一进度跟踪**:在整个过程中使用单一、一致的坐标系统追踪 `compressed_message_count`
3. **添加每聊天锁**:确保每次只有一个后台摘要任务在该聊天中运行
### 中等优先级
4. **保守的裁剪**:精化工具输出裁剪启发式方法,避免更改有效 assistant 内容
5. **定义 `max_context_tokens = 0` 语义**:使行为一致且可预测
6. **修复 i18n**:清理破损的韩文翻译字符串
---
## 环境
- **插件**: async-context-compression
- **OpenWebUI 版本**: 0.8.9
- **操作系统**: Ubuntu 24.04 LTS ARM64
- **报告者**: @dhaern
- **问题日期**: [最近提交]
---
## 参考资源
- [GitHub Issue #56](https://github.com/Fu-Jie/openwebui-extensions/issues/56)
- 插件: `plugins/filters/async-context-compression/async_context_compression.py`

View File

@@ -1,16 +1,15 @@
# Async Context Compression Filter
**Author:** [Fu-Jie](https://github.com/Fu-Jie/openwebui-extensions) | **Version:** 1.3.0 | **Project:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **License:** MIT
**Author:** [Fu-Jie](https://github.com/Fu-Jie/openwebui-extensions) | **Version:** 1.4.0 | **Project:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **License:** MIT
This filter reduces token consumption in long conversations through intelligent summarization and message compression while keeping conversations coherent.
## What's new in 1.3.0
## What's new in 1.4.0
- **Internationalization (i18n)**: Complete localization of user-facing messages across 9 languages (English, Chinese, Japanese, Korean, French, German, Spanish, Italian).
- **Smart Status Display**: Added `token_usage_status_threshold` valve (default 80%) to intelligently control when token usage status is shown.
- **Improved Performance**: Frontend language detection and logging are optimized to be completely non-blocking, maintaining lightning-fast TTFB.
- **Copilot SDK Integration**: Automatically detects and skips compression for copilot_sdk based models to prevent conflicts.
- **Configuration**: `debug_mode` is now set to `false` by default for a quieter production experience.
- **Atomic Message Grouping**: Introduced structure-aware grouping for `assistant-tool-tool-assistant` chains to prevent "No tool call found" errors.
- **Tail Boundary Alignment**: Implemented automatic correction for truncation points to ensure they don't fall inside a tool-calling sequence.
- **Chat Session Locking**: Added a session-based lock to prevent multiple concurrent summary tasks for the same chat ID.
- **Enhanced Traceability**: Improved summary formatting to include message IDs, names, and metadata for better context tracking.
---

View File

@@ -1,18 +1,17 @@
# 异步上下文压缩过滤器
**作者:** [Fu-Jie](https://github.com/Fu-Jie/openwebui-extensions) | **版本:** 1.3.0 | **项目:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **许可证:** MIT
**作者:** [Fu-Jie](https://github.com/Fu-Jie/openwebui-extensions) | **版本:** 1.4.0 | **项目:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **许可证:** MIT
> **重要提示**:为了确保所有过滤器的可维护性和易用性,每个过滤器都应附带清晰、完整的文档,以确保其功能、配置和使用方法得到充分说明。
本过滤器通过智能摘要和消息压缩技术,在保持对话连贯性的同时,显著降低长对话的 Token 消耗。
## 1.3.0 版本更新
## 1.4.0 版本更新
- **国际化 (i18n) 支持**: 完成了所有用户可见消息的本地化,现已原生支持 9 种语言(含中、英、日、韩及欧洲主要语言)
- **智能状态显示**: 新增 `token_usage_status_threshold` 阀门(默认 80%),可以智能控制何时显示 Token 用量状态,减少不必要的打扰
- **性能大幅优化**: 对前端语言检测和日志处理流程进行了非阻塞重构完全不影响首字节响应时间TTFB保持毫秒级极速推流
- **Copilot SDK 兼容**: 自动检测并跳过基于 `copilot_sdk` 模型的上下文压缩,避免冲突
- **配置项调整**: 为了提供更安静的生产环境体验,`debug_mode` 现已默认设置为 `false`
- **原子消息组 (Atomic Grouping)**: 引入结构感知的消息分组逻辑,确保工具调用链被整体保留或移除,彻底解决 "No tool call found" 错误
- **尾部边界自动对齐**: 实现了截断点的自动修正逻辑,确保历史上下文截断不会落在工具调用序列中间
- **会话级异步锁**: 增加了基于 `chat_id` 的后台任务锁,防止同一会话并发触发多个总结任务
- **元数据溯源增强**: 优化了总结输入格式,在总结中保留了消息 ID、参与者名称及关键元数据提升上下文可追踪性
---

Some files were not shown because too many files have changed in this diff Show More