Skip to content

Commit c83ecdd

Browse files
committed
feat: plugin system with hook-based architecture and two built-in plugins
Add a modular plugin system that extends the pipe via lifecycle hooks, priority-based dispatch, and plugin-exported valves. Seven hooks cover the full request lifecycle: on_init, on_models, on_request, on_request_transform, on_emitter_wrap, on_response_transform, and on_shutdown. Built-in plugins (real use cases, not toy examples): - Pipe Stats Dashboard: virtual model exposing a live SSE-powered admin diagnostics dashboard with tiered data collection, multi-worker Redis aggregation, tabbed UI, and an extensible command system (stats, health, config, help). Demonstrates on_models + on_request hooks. - Think Streaming: live reasoning and tool execution display via SSE-backed iframe embeds. Wraps the stream emitter to copy thinking tokens and tool events into a per-session queue, served to the browser through a dynamically-registered FastAPI endpoint on OWUI's own app. Demonstrates the on_emitter_wrap + on_response_transform hooks. Both plugins use the same SSE transport pattern: dynamically registering API routes on OWUI's FastAPI app with ephemeral key authentication, giving pipe plugins a real-time data channel to the browser without any external infrastructure. Also includes: Nagle-style delta coalescing, context budget guards, tool executor improvements, streaming pipeline hardening, and test coverage expansion (3536 tests).
1 parent 07407a4 commit c83ecdd

File tree

116 files changed

+20110
-7614
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

116 files changed

+20110
-7614
lines changed

.github/workflows/bundle.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ jobs:
2525

2626
- name: Run bundler
2727
run: |
28-
python scripts/bundle.py
29-
python scripts/bundle.py --compress
28+
python scripts/bundle_v2.py
29+
python scripts/bundle_v2.py --compress
3030
3131
- name: Verify bundled files
3232
run: |

.github/workflows/ci.yml

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,24 +36,44 @@ jobs:
3636
uv pip install --python .venv/bin/python torch --index-url https://download.pytorch.org/whl/cpu
3737
# Install open-webui and project dependencies
3838
uv pip install --python .venv/bin/python open-webui -e .
39-
uv pip install --python .venv/bin/python pytest pytest-asyncio pyright cairosvg pyzipper aioresponses
39+
uv pip install --python .venv/bin/python pytest pytest-asyncio pyright cairosvg pyzipper aioresponses ruff
4040
41-
- name: Run tests and checks
41+
- name: Lint source code
4242
run: |
4343
source .venv/bin/activate
44-
# Build monolith bundles (used by Open WebUI "Import from Link")
45-
python scripts/bundle.py
46-
python scripts/bundle.py --compress
44+
ruff check open_webui_openrouter_pipe/
45+
46+
- name: Build monolith bundles
47+
run: |
48+
source .venv/bin/activate
49+
python scripts/bundle_v2.py
50+
python scripts/bundle_v2.py --compress
51+
52+
- name: Lint bundles
53+
run: |
54+
source .venv/bin/activate
55+
ruff check open_webui_openrouter_pipe_bundled.py
56+
ruff check --per-file-ignores '*_compressed.py:E402' open_webui_openrouter_pipe_bundled_compressed.py
57+
58+
- name: Type-check source and bundles
59+
run: |
60+
source .venv/bin/activate
61+
pyright
62+
pyright open_webui_openrouter_pipe_bundled.py
63+
pyright open_webui_openrouter_pipe_bundled_compressed.py
64+
65+
- name: Run tests
66+
run: |
67+
source .venv/bin/activate
68+
python -m compileall open_webui_openrouter_pipe tests filters open_webui_openrouter_pipe_bundled.py open_webui_openrouter_pipe_bundled_compressed.py
4769
4870
# Run the test suite in 3 modes:
4971
# 1) Normal package imports (on-disk modules)
50-
# 2) Bundled raw monolith
72+
# 2) Bundled flat monolith
5173
# 3) Bundled compressed monolith
5274
echo "==> pytest (package imports)"
5375
PYTHONPATH=. pytest tests -q
54-
echo "==> pytest (bundled: open_webui_openrouter_pipe_bundled.py)"
76+
echo "==> pytest (bundled flat: open_webui_openrouter_pipe_bundled.py)"
5577
OWUI_PIPE_BUNDLE_PATH=open_webui_openrouter_pipe_bundled.py PYTHONPATH=. pytest tests -q
56-
echo "==> pytest (bundled: open_webui_openrouter_pipe_bundled_compressed.py)"
78+
echo "==> pytest (bundled compressed: open_webui_openrouter_pipe_bundled_compressed.py)"
5779
OWUI_PIPE_BUNDLE_PATH=open_webui_openrouter_pipe_bundled_compressed.py PYTHONPATH=. pytest tests -q
58-
python -m compileall open_webui_openrouter_pipe tests filters
59-
pyright

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@ cliff.toml
2020
plans/
2121
open_webui_openrouter_pipe_bundled.py
2222
open_webui_openrouter_pipe_bundled_compressed.py
23+
scripts/deploy_dev.sh

README-dev.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,20 @@ Go to **Actions** → select a workflow run → scroll to **Artifacts** → down
143143

144144
---
145145

146+
## Linting
147+
148+
Production code is linted with [ruff](https://docs.astral.sh/ruff/) in CI. No config file is needed — the command scopes itself:
149+
150+
```bash
151+
source .venv/bin/activate && ruff check open_webui_openrouter_pipe/
152+
```
153+
154+
Tests (`tests/`) are **not** linted — patterns like mid-file imports, fixture redefinitions, and assign-to-verify are intentional and would trigger false positives.
155+
156+
> **Warning:** Do not use `ruff --fix`. It cannot detect re-exports and will silently remove imports that other modules depend on. Fix issues manually.
157+
158+
---
159+
146160
## Key Insight
147161

148162
**Branches are for work, tags are for releases.**

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# Open WebUI → OpenRouter Pipe
22

33
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4-
[![Version](https://img.shields.io/badge/version-2.2.1-blue.svg)](https://github.com/rbb-dev/Open-WebUI-OpenRouter-pipe)
5-
[![Open WebUI Compatible](https://img.shields.io/badge/Open%20WebUI-0.7.0%2B-green.svg)](https://openwebui.com/)
4+
[![Version](https://img.shields.io/badge/version-2.2.11-blue.svg)](https://github.com/rbb-dev/Open-WebUI-OpenRouter-pipe)
5+
[![Open WebUI Compatible](https://img.shields.io/badge/Open%20WebUI-0.8.0%2B-green.svg)](https://openwebui.com/)
66

77
**Access 350+ AI models through one interface.**
88

@@ -70,6 +70,9 @@ Optional encrypted session logs for incident response. Request identifiers flow
7070
🛡️ **Enterprise Controls**
7171
Encryption, retention policies, request attribution, and operational hooks your governance program can build on.
7272

73+
🔌 **Plugin System**
74+
Extend the pipe with custom plugins — inject virtual models, intercept requests, transform payloads before OpenRouter, and post-process responses before delivery. Ships with the Pipe Stats Dashboard plugin as a reference implementation. Disabled by default; enable via valves. See [Plugin System Guide](docs/plugin_system.md).
75+
7376
---
7477

7578
## Quick Start
@@ -133,7 +136,7 @@ That's it. Start talking.
133136

134137
## Requirements
135138

136-
- Open WebUI 0.7.0+
139+
- Open WebUI 0.8.0+
137140
- An [OpenRouter](https://openrouter.ai/) account
138141
- `WEBUI_SECRET_KEY` configured (required for encrypted credential storage)
139142

docs/README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Use these docs if you are modifying the pipe, working on tests, or extending the
2020
## Quick start (install and use)
2121

2222
### Prerequisites
23-
- Open WebUI `0.7.0` or later (the pipe declares this requirement in its manifest header).
23+
- Open WebUI `0.8.0` or later (the pipe declares this requirement in its manifest header).
2424
- An OpenRouter API key, provided either via the `OPENROUTER_API_KEY` environment variable or via the pipe’s valve configuration.
2525

2626
### Install (Open WebUI UI)
@@ -115,5 +115,8 @@ See: [Model Variants & Presets](model_variants_and_presets.md).
115115
- [Multimodal Ingestion Pipeline](multimodal_ingestion_pipeline.md)
116116
- [Task Models & Housekeeping](task_models_and_housekeeping.md)
117117
- [Tooling & Integrations](tooling_and_integrations.md)
118+
- [Plugin System — Developer Guide](plugin_system.md) — hook-based plugin architecture for extending pipe behavior (model injection, request interception, request/response transformation).
119+
- [Pipe Stats Dashboard Plugin](plugins_pipe_stats.md) — built-in plugin: virtual model with live SSE dashboard for admin diagnostics.
120+
- [Think Streaming Plugin](plugins_think_streaming.md) — built-in plugin: live reasoning and tool execution display via SSE-powered iframe embeds.
118121
- [Test Suite Audit Snapshot](test_suite_audit_snapshot.md) — one-shot snapshot of a completed test audit; not maintained.
119122
- `CHANGELOG.md` (repo root) — audit trail for changes.

docs/developer_guide_and_architecture.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ At a high level, a request follows this shape:
6363

6464
4. **Tool-call loop (between Responses calls)**
6565
- When a Responses run completes, the pipe inspects the returned `output` items.
66-
- `function_call` items are executed locally against the Open WebUI tool registry, converted into `function_call_output` items, appended to the next request’s `input[]`, and the loop continues until no further tool calls are produced or `MAX_FUNCTION_CALL_LOOPS` is reached.
66+
- `function_call` items are executed locally against the Open WebUI tool registry, converted into `function_call_output` items, appended to the next request’s `input[]`, and the loop continues until no further tool calls are produced or `MAX_FUNCTION_CALL_LOOPS` is reached (at which point the model gets a synthesis turn). Applies only when `TOOL_EXECUTION_MODE="Pipeline"`.
6767

6868
5. **Persistence (optional)**
6969
- Depending on valves, artifacts (reasoning/tool outputs) are persisted to SQL storage (optionally encrypted and/or compressed) and may be cached in Redis in multi-worker configurations.

docs/model_catalog_and_routing_intelligence.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ Behavior:
6868
- `meta.profile_image_url` (model icon as a PNG data URL), and
6969
- `meta.description` (model description text).
7070
This behavior is controlled by `UPDATE_MODEL_CAPABILITIES`, `UPDATE_MODEL_IMAGES`, and `UPDATE_MODEL_DESCRIPTIONS`. See: [OpenRouter Integrations & Telemetry](openrouter_integrations_and_telemetry.md).
71-
- New model access control defaults are set **on insert only**: `NEW_MODEL_ACCESS_CONTROL` determines whether newly inserted OpenRouter overlays are public (`access_control=None`) or private (`access_control={}`), with the `admins` option relying on Open WebUIs `BYPASS_ADMIN_ACCESS_CONTROL` for admin access.
71+
- New model access control defaults are set **on insert only**: `NEW_MODEL_ACCESS_CONTROL` determines whether newly inserted OpenRouter overlays are public (wildcard read grant) or private (no access grants), with the `admins` option relying on Open WebUI's `BYPASS_ADMIN_ACCESS_CONTROL` for admin access.
7272

7373
---
7474

0 commit comments

Comments
 (0)