You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Final pre-release hardening for v1.6.1, found by a multi-agent audit:
Security:
- Add allow_redirects=False to all four OpenRouter HTTP calls (/models,
/chat/completions, provider-registry fetch, ZDR /endpoints/zdr fetch).
v1.3-1.6 added two new GETs that lacked the guard; with requests>=2.32.4
this closes the Authorization-header redirect-leak class (CVE-2024-35195).
Changed:
- FREE_MODEL_FILTER default now honours the legacy OPENROUTER_FREE_ONLY=true
env var (maps to "only") so upgrades from the FREE_ONLY era don't silently
return paid models.
Fixed:
- function.json version 1.6.0 -> 1.6.1 (matched the code) + updated_at bump.
- Docs: replace all FREE_ONLY references in README/TESTING with
FREE_MODEL_FILTER, add a migration note, add the missing SHOW_COST_INFO /
COST_CURRENCY valves to the config table, expand the Features list (ZDR,
tool-calling filter, provider preferences), and correct test counts (557
unit / 44 integration).
Test suite: 557 passing; mypy + pyflakes clean.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: CHANGELOG.md
+13Lines changed: 13 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
8
8
## [Unreleased]
9
9
10
+
### Security
11
+
12
+
-**No redirect following on any OpenRouter call** — `allow_redirects=False` is now passed to all four HTTP requests (`/models`, `/chat/completions`, the provider-registry fetch, and the ZDR `/endpoints/zdr` fetch). Combined with the `requests>=2.32.4` floor, this prevents the `Authorization: Bearer` header from being forwarded to a redirect target (CVE-2024-35195 family) if the base URL is misconfigured
13
+
14
+
### Changed
15
+
16
+
-**`FREE_MODEL_FILTER` honours the legacy `OPENROUTER_FREE_ONLY` env var** — when `OPENROUTER_FREE_MODEL_FILTER` is unset, `OPENROUTER_FREE_ONLY=true` now maps to `only`, so installs upgrading from the pre-1.5 `FREE_ONLY` era don't silently start returning paid models
17
+
18
+
### Fixed
19
+
20
+
-**Version metadata** — `function.json` reported `1.6.0` while the code was `1.6.1`; bumped the manifest version (and `updated_at`) to match
21
+
-**Docs: `FREE_ONLY` → `FREE_MODEL_FILTER`** — README and TESTING.md still referenced the removed `FREE_ONLY` valve; updated all references, added a migration note, documented the missing `SHOW_COST_INFO` / `COST_CURRENCY` valves, and corrected the test counts
Copy file name to clipboardExpand all lines: README.md
+22-9Lines changed: 22 additions & 9 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -66,8 +66,12 @@ cache control out of the box.
66
66
-**Cache control** — Anthropic-style `cache_control` injection on the longest message chunk.
67
67
-**Citations** — `[n]` references from web-search-enabled models are converted to markdown links.
68
68
-**Provider icons** — 13 hardcoded fast-path logos plus auto-discovered icons for ~20 more providers (xAI, Inflection, NVIDIA, Arcee, Morph, Cerebras, …) lazy-loaded from OpenRouter's provider registry, all synced directly into Open WebUI's model database.
69
-
-**Retry logic** — exponential backoff with jitter on timeout and connection errors.
70
-
-**FREE_ONLY mode** — filter to show only free-tier models (`:free` suffix or `0/0` pricing).
69
+
-**ZDR (Zero Data Retention)** — filter the catalog to ZDR-capable models (`ZDR_MODELS_ONLY`) and/or enforce ZDR per request (`ZDR_ENFORCE`).
70
+
-**Tool-calling filter** — show all / only / exclude tool-capable models (`TOOL_CALLING_FILTER`).
python test_pipe.py #431 tests — verify everything is green
110
+
python test_pipe.py #557 tests — verify everything is green
107
111
```
108
112
109
113
## Usage
@@ -116,7 +120,7 @@ an environment variable fallback (see [Configuration](#configuration)).
116
120
| Goal | Valves to set |
117
121
| --- | --- |
118
122
| Show only OpenAI and Anthropic models |`MODEL_PROVIDERS = openai,anthropic`|
119
-
| Show only free models |`FREE_ONLY = true`|
123
+
| Show only free models |`FREE_MODEL_FILTER = only`|
120
124
| Use DeepSeek for reasoning | select `deepseek/deepseek-r1`, `INCLUDE_REASONING = true`|
121
125
| Route cheapest provider first |`PROVIDER_SORT = price`|
122
126
| Add a fallback model |`FALLBACK_MODELS = anthropic/claude-3.5-sonnet`|
@@ -213,6 +217,15 @@ Every valve accepts an environment variable fallback. The table below lists both
213
217
|`MAX_RETRIES`| — |`2`| Auto-retry count on transient errors |
214
218
|`HTTP_REFERER_OVERRIDE`|`OPENROUTER_HTTP_REFERER`|`""`| Override the `HTTP-Referer` header sent to OpenRouter (must include scheme). Empty falls back to `WEBUI_URL`|
215
219
220
+
### Cost Display
221
+
222
+
| Valve | Env Var | Default | Description |
223
+
| --- | --- | --- | --- |
224
+
|`SHOW_COST_INFO`| — |`false`| Append token usage and cost to each response (also requests `usage` so streaming responses include cost) |
225
+
|`COST_CURRENCY`|`OPENROUTER_COST_CURRENCY`|`USD`| Currency label for the cost display (display only; OpenRouter bills in USD) |
226
+
227
+
> **Migration (v1.5.0):** the old boolean `FREE_ONLY` valve was replaced by `FREE_MODEL_FILTER` (`all` / `only` / `exclude`). Set `FREE_MODEL_FILTER = only` to preserve the old `FREE_ONLY = true` behaviour. For backward compatibility, the legacy `OPENROUTER_FREE_ONLY=true` environment variable is still honoured when `FREE_MODEL_FILTER` is unset.
228
+
216
229
## Architecture
217
230
218
231
The pipe implements the **Manifold** pattern: one pipe entry point that surfaces multiple models.
@@ -230,8 +243,8 @@ The pipe implements the **Manifold** pattern: one pipe entry point that surfaces
230
243
Open-WebUI-Pipe-OpenRouter/
231
244
├── openrouter_pipe.py # Main pipe source — install this in Open WebUI
232
245
├── function.json # Open WebUI community manifest
233
-
├── test_pipe.py # Unit test suite (431 tests)
234
-
├── integration_test.py # Live API integration tests (43 assertions)
246
+
├── test_pipe.py # Unit test suite (557 tests)
247
+
├── integration_test.py # Live API integration tests (44 assertions)
235
248
├── TESTING.md # Manual pre-release checklist
236
249
├── SECURITY.md # Security policy
237
250
├── CONTRIBUTING.md # Contribution guidelines
@@ -260,7 +273,7 @@ It also removes `user` when sent as a dict (Open WebUI format) since OpenRouter
260
273
## Development
261
274
262
275
```bash
263
-
python test_pipe.py # Unit tests (431 tests)
276
+
python test_pipe.py # Unit tests (557 tests)
264
277
python integration_test.py # Live API tests (requires OPENROUTER_API_KEY)
265
278
```
266
279
@@ -313,7 +326,7 @@ reasoning models can take over a minute for complex prompts.
313
326
314
327
1. Verify your API key is valid (a single "error" model appears if it is not).
315
328
2. If `MODEL_PROVIDERS` is set, confirm the provider names are lowercase: `openai`, `anthropic`, `google`.
316
-
3. If `FREE_ONLY` is enabled, some providers may have no free models — try disabling it.
329
+
3. If `FREE_MODEL_FILTER = only` is set, some providers may have no free models — set it back to `all`.
317
330
4. Set `MODEL_PROVIDERS = ALL` to show the full catalog.
318
331
319
332
### Models load but chat returns errors
@@ -333,7 +346,7 @@ re-invokes the pipe with the updated thread. The pipe forwards the full message
333
346
OpenRouter on each invocation. Whether a model can generate tool calls depends on
334
347
OpenRouter's provider support for that model.
335
348
336
-
**Q: Why does `FREE_ONLY` include models without a `:free` suffix?**
349
+
**Q: Why does `FREE_MODEL_FILTER = only` include models without a `:free` suffix?**
337
350
338
351
A: Some models are listed as free on OpenRouter without carrying a `:free` suffix in their
339
352
ID. The pipe uses a two-pass check: first it looks for the `:free` suffix, then it falls
Copy file name to clipboardExpand all lines: TESTING.md
+7-5Lines changed: 7 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -91,8 +91,10 @@ Must exit with `All tests passed! ✓` and `✗ Failed: 0`. If any test fails, *
91
91
| 7.1 | Set `MODEL_PROVIDERS = openai`| Only OpenAI models visible in selector |
92
92
| 7.2 | Set `MODEL_PROVIDERS = openai` + `INVERT_PROVIDER_LIST = true`| All models **except** OpenAI visible |
93
93
| 7.3 | Set `MODEL_PROVIDERS = ALL` or clear the field |**All** models visible again. Default `ALL` means no filter |
94
-
| 7.4 | Set `FREE_ONLY = true`| Only free models (including those with pricing 0/0 without `:free` suffix) |
95
-
| 7.5 |`FREE_ONLY = true` > verify that free `google/gemma-*` or `qwen/qwen3-*` appear | Models without `:free` but with pricing 0/0 are included |
94
+
| 7.4 | Set `FREE_MODEL_FILTER = only`| Only free models (including those with pricing 0/0 without `:free` suffix) |
95
+
| 7.5 |`FREE_MODEL_FILTER = only` > verify that free `google/gemma-*` or `qwen/qwen3-*` appear | Models without `:free` but with pricing 0/0 are included |
96
+
| 7.6 | Set `FREE_MODEL_FILTER = exclude`| Free models hidden; only paid models remain |
97
+
| 7.7 | Set env `OPENROUTER_FREE_ONLY=true` (legacy), leave `FREE_MODEL_FILTER` unset | Back-compat: behaves like `only`|
96
98
97
99
---
98
100
@@ -194,14 +196,14 @@ Must exit with `All tests passed! ✓` and `✗ Failed: 0`. If any test fails, *
194
196
195
197
## Quick pre-release checklist
196
198
197
-
-[ ]`python test_pipe.py` → 431 passed, 0 failed
198
-
-[ ]`python integration_test.py` → 43/43
199
+
-[ ]`python test_pipe.py` → 557 passed, 0 failed
200
+
-[ ]`python integration_test.py` → 44/44
199
201
-[ ] Empty API key → clear error message in model selector
200
202
-[ ] Valid API key → 340+ models with provider icons
0 commit comments