Releases: sena-labs/Open-WebUI-Pipe-OpenRouter
v1.10.4 — Funding to Ko-fi
Funding source switched to Ko-fi only: https://ko-fi.com/senalabs
.github/FUNDING.ymlrewritten toko_fi: senalabsfunding_urlinfunction.jsonand the module-level frontmatter docstring point tohttps://ko-fi.com/senalabs- GitHub Sponsors link removed
No code change. All 939 unit tests still green.
v1.10.3 — Portal Upload Fix (frontmatter refresh)
Patch release that supersedes v1.10.2 and fixes the openwebui.com portal upload for real.
What v1.10.2 got wrong
Swapped the function id to openrouter-pipe on the assumption the portal expected dashes. OWUI source proves otherwise — src/lib/utils/index.ts:
export const nameToId = (name: string): string =>
name.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '')
.replace(/[^\w]+/g, '_') // \w = [A-Za-z0-9_] → dashes → underscores
.replace(/^_+|_+$/g, '')
.toLowerCase();OpenRouter Pipe → openrouter_pipe. Every existing community pipe under /f/<user>/<slug> uses underscores. v1.10.3 reverts to the historical id.
The actual portal error
backend/open_webui/utils/plugin.py:extract_frontmatter parses the module-level docstring at the top of openrouter_pipe.py against ^\s*([a-z_]+):\s*(.*)$ and ships those keys to the portal preview. The docstring still claimed version: 1.9.0 and a description from before the v1.10.x image / video / audio output flows, so the portal surfaced the stale/missing keys as a JavaScript undefined tooltip.
Fixed in 1.10.3
function.jsonid reverted toopenrouter_pipe, version →1.10.3.- Module-level frontmatter docstring rewritten:
version: 1.10.3+ a v1.10.x-accurate description (image-gen, video-gen, audio-gen embed mechanics, SSRF-guarded media downloads, 99.3% icon coverage,MAX_TOOL_ITERATIONS, encrypted UserValves keys, atomic routing-set swap). Unicode arrow→replaced with..to avoid non-ASCII edge cases in the portal's tooltip renderer. - Docstring examples in
_clean_model_idand_sync_model_iconsreverted toopenrouter_pipe.openai/gpt-4o.
Migration for v1.10.2 testers
If you tagged or installed 1.10.2 between releases your OWUI DB has a stranded function row under openrouter-pipe. To clean up:
- Copy your Valves config.
- Admin Panel → Functions → delete the
openrouter-pipeentry. - Install/update
openrouter_pipeto 1.10.3. - Paste your Valves config back.
No chat history is lost (it is keyed on the model selector entry, not the function row).
Tests
All 939 unit tests still green on Python 3.10–3.13.
v1.10.2 — Function ID Portal-Slug Fix
Patch release fixing the openwebui.com community portal upload.
What changed
function.json id renamed from openrouter_pipe to openrouter-pipe so it matches the slug the portal auto-generates from the listing title ("OpenRouter Pipe"). The portal previously rejected the underscored manifest with an undefined error.
Breaking change for existing Admin-Panel installs
Open WebUI keys functions by ID in its database. After this update:
- The old
openrouter_pipefunction row remains active in your DB. - The new
openrouter-pipeID will create a fresh function row when re-installed from the portal.
Migration (no chat history is lost — it's tied to the model selector entry, not the function row):
- Copy your current Valves configuration.
- Uninstall the old
openrouter_pipefunction from Admin Panel -> Functions. - Install the new
openrouter-pipefrom the openwebui.com portal (or pasteopenrouter_pipe.pyv1.10.2). - Paste your Valves configuration back.
Tests
All 939 unit tests still green.
Other changes
- Docstring references in
_clean_model_idand_sync_model_iconsnow show the dash form when illustrating the OWUI manifold prefix (e.g.openrouter-pipe.openai/gpt-4o). - 5
test_pipe.pysimulations of_function_idand 2 prefix-assertion strings updated to match the new ID.
v1.10.1 — Icon Coverage 99.3%
Patch release covering the 10 icon-system commits between v1.10.0 and now. Live-VPS brand-icon coverage went from 261/408 (64%) to 448/451 (99.3%) — only the three kwaivgi/kling-* models remain on the deterministic letter-SVG fallback because Kuaishou's video team has no public icon source.
Added
- Layered icon-resolution fallback chain (registry → hyphen-strip → hardcoded → alias → provider-domain → letter-SVG)
- 41 official brand / HuggingFace-avatar icons hardcoded in
_PROVIDER_ICONS USE_PROVIDER_DOMAIN_FAVICONadmin valve (default true) — privacy-friendlier than gstatic_generate_letter_icondeterministic last-resort fallback_sync_orphan_db_iconssweep — patches OWUI rows for deprecated/withdrawn models the regular sync skips
Fixed
/static/favicon.pngnow recognised as managed (OWUI 0.4+ server default)Models.{get,update,insert}_model_by_idasync on OWUI ≥ 0.4 — new_resolve_maybe_awaitableresolver- Provider-domain favicon URLs that returned text/html (SPA shell) for ai21 / bytedance / mancer / openrouter swapped to HuggingFace avatars
Tests
868 → 939 (+71). All green on Python 3.10–3.13.
Migration
No breaking changes. Replace openrouter_pipe.py in Admin Panel → Functions → OpenRouter Pipe, or pull v1.10.1 from GitHub. The icon sync runs automatically on the next pipes() call.
v1.10.0 — Image / Video / Audio Generation + 5-Agent Audit
The definitive OpenRouter integration for Open WebUI — now with full media-generation support and a hardened security/perf surface from a multi-pass audit.
New capabilities
- Image generation (
flux,gemini-image-preview, …) —data:URLs decoded, uploaded to OWUI storage, rendered inline as. Works on stream, non-stream, and native-tool paths. - Video generation (
google/veo-3.1-*,kwaivgi/kling-*,openai/sora-*,bytedance/seedance-*,minimax/hailuo-*,alibaba/wan-*,x-ai/grok-imagine-video) — asyncPOST /api/v1/videoswith polling, MP4 re-hosted, embedded as native<video controls>via OWUI's block-HTML token. ConfigurableVIDEO_POLL_INTERVAL(5 s) +VIDEO_GENERATION_TIMEOUT(600 s). - Audio generation (
google/lyria-3-*-preview,openai/gpt-audio*) — pipe injectsmodalities=[\"text\",\"audio\"]+audio={format,voice}+stream=trueautomatically. OpenAI'sgpt-audiofamily auto-forced topcm16and wrapped in a RIFF/WAVE container (24 kHz mono) so the browser plays it; Lyria gets mp3. NewAUDIO_OUTPUT_FORMAT+AUDIO_OUTPUT_VOICEvalves.
Security hardening (multi-agent audit batches A–E)
- SSRF / auth-leak guard —
polling_urlandunsigned_urls[0]restricted toopenrouter.ai(_is_openrouter_url); refuses to send the Authorization bearer to attacker-controlled hosts. - Byte-size caps — 100 MiB video / 50 MiB audio, with streaming
iter_content+ Content-Length early-reject + mid-stream cap. - MIME whitelist post-download —
mp4/webm/mov/mkvfor video,mpeg/wav/flac/ogg/opus/aac/mp4for audio; spoofedContent-Typecan no longer coerce OWUI's renderer. - Citation URL scheme filter —
_emit_citation_eventsrefuses non-http(s)URLs (closes thejavascript:/data:XSS surface). - Admin-only ZDR —
ZDR_ENFORCEremoved fromUserValves; privacy policy is no longer user-overridable. - Sanitized internal exceptions — generic
Exceptionno longer leaks raw Python stack traces / file paths to the chat client.
Correctness hardening
- Body deep-copy —
pipe()deep-copies the body before any modality injection; OWUI's body dict is never mutated (closes a CRIT cross-request leak). - Atomic routing-set swap —
_video_model_ids/_audio_model_idsare frozensets, swapped in one assignment; concurrentpipe()callers never see an empty intermediate set. - Lazy populate off the event loop — first request after restart triggers
self.pipes()viaasyncio.to_thread, gated by_lazy_populated. - Resource leaks —
submit_respand per-iterationpoll_respin the video flow are now wrapped intry/finallywithclose()(previously up to 120 socket leaks per long job). - Tool-nonstream clean assistant msg — appends
{role, content, tool_calls}only, instead of forwarding the raw upstream dict (which can carryrefusal/ legacyfunction_callkeys some models reject on re-submission).
Performance
- HTTPAdapter pool sized for concurrency —
pool_connections=64,pool_maxsize=64; default urllib3 pool of 10 was a hard limit under load. - Cached Authorization header —
EncryptedStr.decrypt()(Fernet, ~100 µs) now runs once per ciphertext instead of on every chunk send / poll / footer; auto-invalidates on key rotation. - Cached-only credit footer —
_credit_balance_cachedreads from the pre-warmed cache; SSE finalize never blocks on a coldGET /credits. _effective_valvesfast path — returnsself.valvesdirectly when no UserValves override._models_cache_validshort-circuit — cheap TTL check before Fernet+SHA256.- Hot-path constants —
_DATA_IMAGE_RE, MIME whitelists, byte caps,_AUDIO_FORMAT_TO_MIMEhoisted to module level.
Tests
868 tests (up from 727), all green on Python 3.10–3.13.
Migration
No breaking changes. Existing installs upgrade in place — the new valves all have safe defaults. To pick up the changes, replace openrouter_pipe.py in Admin Panel → Functions → OpenRouter Pipe (or pull the v1.10.0 tag from GitHub).
Full diff: `v1.3.0...v1.10.0` (the previous release tag — code shipped across all intermediate versions documented in CHANGELOG.md).
v1.3.0 — Provider Icon Sync, Audio & Image Output, 340+ Models
What's new in v1.3.0
✨ New features
Provider icon sync
Provider logos (OpenAI, Anthropic, Google, Meta, Mistral, DeepSeek, Cohere, Perplexity, Qwen, Microsoft, Fireworks, Moonshot, Amazon) are now written directly into Open WebUI's model database on startup. Icons appear in the model selector without any manual configuration and survive cache refreshes. Five bugs that silently prevented icons from ever appearing after the first load have been fixed.
Audio output support
Models that return audio (e.g. openai/gpt-4o-audio-preview) now surface their transcript as readable text in both streaming and non-streaming mode. Previously these responses produced an empty reply.
Image output support
Models that generate images (e.g. google/gemini-2.5-flash-image-preview) embed the result as a markdown image directly in the chat. URLs are validated — only http:// and https:// schemes are rendered; invalid URLs are silently dropped.
Token usage & cost display
Non-streaming responses append a compact footer — Tokens: 312 in / 84 out · Cost: $0.0004 — when OpenRouter includes usage data. Useful for tracking spend per message without leaving Open WebUI.
🔧 Also in this release (carried from v1.2.0, now officially tagged)
- Connection pooling via
requests.Session— better throughput for multi-turn conversations - Model list caching with 5-minute TTL and automatic invalidation when valves change
- Exponential backoff with jitter on transient network errors (
min(2^n + rand, 30s)) - Fallback model deduplication — silently removes duplicate IDs from
FALLBACK_MODELS - Base URL validation — Pydantic rejects non-HTTP(S) schemes at save time
- "error" model guard — selecting the error pseudo-model returns a clear message instead of hitting the API
- Empty message guard — returns a clear error instead of sending an empty payload
- Fallback attribution — non-stream replies show
Responded by: model-idwhen a fallback handled the request - HTTP 502 auth detection — Clerk 502 errors (malformed API key) caught at model-list time
🐛 Bug fixes
_sync_model_icons(): wrong skip condition caused all icons to be silently skipped (any existing icon, including the OWUI default SVG, blocked the update)_sync_model_icons(): race condition — icons written beforepipes()returned were overwritten by OWUI's default; now also syncs on cache-hit paths until all icons are confirmed_sync_model_icons(): DB errors no longer mark a model as permanently synced, allowing retry on next request_sync_model_icons():update_model_by_idno longer clobbers user-set temperature / system prompt with an emptyModelParams()_sync_model_icons():function_idcached once at__init__instead of recomputed on every call- Streaming "done" status event now emitted correctly (async generator replaces sync generator that could not
await) pipes():response.close()guaranteed in all code paths viafinallyblock- Image markdown: invalid URL schemes dropped silently instead of producing broken
![]()tags - Payload deep-copy prevents mutation when
ENABLE_CACHE_CONTROLis active IndexErroron stream chunks with emptychoicesarray- Stream error handler caches response body before closing connection
📦 Install / upgrade
Open WebUI Community hub — search OpenRouter Pipe and click Update.
Manual — replace the pipe content in Admin Panel → Functions with the latest openrouter_pipe.py.
From source
```bash
git pull origin main
python test_pipe.py # 431/431 ✓
```
No valve changes required — all new features activate automatically or via existing valves (SYNC_PROVIDER_ICONS to control icon sync, default: enabled).
✅ Compatibility
| Requirement | Minimum |
|---|---|
| Open WebUI | 0.4.0 |
| Python | 3.10 |
| requests | 2.20 |
| pydantic | 2.0 |
Full changelog: v1.1.0 → v1.3.0
v1.1.0
What's New
Added
- Password-masked API key - valve settings now use a secure password field (Open WebUI v0.8+)
- Native dropdown menus - Reasoning Effort, Provider Sort, and Data Collection valves use select widgets instead of free text
- Event emitter support -
__event_emitter__shows 'Querying OpenRouter...' status in chat UI for non-stream requests - Additional defensive keys -
metadata,files,tool_ids,session_id,message_idstripped from payload before forwarding to OpenRouter
Tests
- 23 new unit tests (193 total, 0 failures)
- New section 16: Valve
json_schema_extravalidation - Event emitter coverage for stream and non-stream paths
Docs
- README updated: version badge,
pipe()signature, internal keys set, test count - CHANGELOG formatting fixes (MD022/MD032)
Full Changelog: v1.0.0...v1.1.0